无法序列化HibernateProxy的对象原因

我收到来自服务器的以下错误响应。

HTTP状态500 –

键入例外报告

信息

描述服务器遇到一个内部错误(),阻止它履行这个请求。

例外

javax.servlet.ServletException:java.lang.UnsupportedOperationException:试图序列化java.lang.Class:org.hibernate.proxy.HibernateProxy。 忘了注册一个types适配器?

根本原因

java.lang.UnsupportedOperationException:试图序列化java.lang.Class:org.hibernate.proxy.HibernateProxy。 忘了注册一个types适配器?

从Javadebugging器:

org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer@7632012e 

我正在使用Gson将我的Java对象转换为JSON。 下面我粘贴了一些我的代码。

这是我的资源:

 @Stateless @LocalBean @Path("/autos") @Produces(MediaType.APPLICATION_JSON) public class AutoResource { @EJB private CarAssembler warehouse; @Context private UriInfo uriInfo; @GET public Response allAutos() { // Building a context, lots of code... // Creating a Gson instance and configures it... final Auto auto = warehouse.list(context); final String autoJson = gson.toJson(auto); return Response.ok(autoJson).build(); } } 

CarAssembler只是一个调用存储库的服务。 我没有在这里粘贴服务的代码。

库:

 @Override public Question findById(final int id, final FetchType fetchType) { final Auto question = getEntityManager().find(Auto.class, id); if (fetchType == FetchType.LAZY) { return auto; } Hibernate.initialize(auto.getManufacturer()); Hibernate.initialize(auto.getAssemblyHouse()); return auto; } 

正如你所看到的,我提供了懒惰和渴望加载的对象。 我使用Hibernate.initialize来提前获取JPA关联。 但是,问题是我如何解决我得到的代理错误。 为什么到那里只有AssemblyHouse仍然附加到JavaAssist,而制造商不是(我已经看到了Javadebugging器中的types)。 我如何知道何时取消代理对象? 我应该unproxy这个汽车可能有的所有关联? 而在我的代码层? 当我unproxy它会影响我的应用程序的性能? 还有其他解决scheme吗? 我从错误消息中看到,我可以创build一个types适配器。 是的,我可以,但是我必须为所有的域对象做这件事,以确保转换正确完成。 当我尝试将其转换为JSON表示forms时,也许在我的域中的其他对象开始失败,但我不知道何时或为什么。 其他物体是好还是好运?

这就是我不讨厌的对象,但是我还没有实现它,因为我不知道这是好还是坏,以及在哪一层做这件事,什么时候该做。 我应该一直unproxy对象?

 public class HibernateUtilities { public static <T> T unproxy(T proxy) { if (proxy == null) { return null; } if (proxy instanceof HibernateProxy) { Hibernate.initialize(proxy); HibernateProxy hibernateProxy = (HibernateProxy) proxy; T unproxiedObject = (T) hibernateProxy.getHibernateLazyInitializer().getImplementation(); return unproxiedObject; } return proxy; } } 

Stacktrace按要求:

 [#| 2012-11-22T17:17:13.285 + 0100 |警告| glassfish3.1.2 | javax.enterprise.system.container.web.com.sun.enterprise.web | _ThreadID = 71; _ThreadName =螺纹-8; | StandardWrapperValve [javax.ws.rs.core.Application]:
 PWC1406:用于servlet javax.ws.rs.core.Application的Servlet.service()
抛出exceptionjava.lang.UnsupportedOperationException:尝试
序列化java.lang.Class:org.hibernate.proxy.HibernateProxy。 忘记
注册一个types适配器?
    在com.google.gson.internal.bind.TypeAdapters $ 1.write(TypeAdapters.java:64)
    在com.google.gson.internal.bind.TypeAdapters $ 1.write(TypeAdapters.java:61)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.ArrayTypeAdapter.write(ArrayTypeAdapter.java:93)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.CollectionTypeAdapterFactory $ Adapter.write(CollectionTypeAdapterFactory.java:96)
    在com.google.gson.internal.bind.CollectionTypeAdapterFactory $ Adapter.write(CollectionTypeAdapterFactory.java:60)
    在com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)
    在com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    在com.google.gson.Gson.toJson(Gson.java:586)
    在com.google.gson.Gson.toJson(Gson.java:565)
    在com.google.gson.Gson.toJson(Gson.java:520)
    在com.myapp.AutoResource.produceAuto(AutoResource.java:48)
    在sun.reflect.NativeMethodAccessorImpl.invoke0(本地方法)
    在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    在java.lang.reflect.Method.invoke(Method.java:601)
    在org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052)
    在org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124)
    在com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5388)
    在com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:619)
    在com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)
    在com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:571)
    在com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doAround(SystemInterceptorProxy.java:162)
    在com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:144)
    在sun.reflect.NativeMethodAccessorImpl.invoke0(本地方法)
    在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    在java.lang.reflect.Method.invoke(Method.java:601)
    在com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:861)
    在com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)
    在com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:370)
    在com.sun.ejb.containers.BaseContainer .__拦截(BaseContainer.java:5360)
    在com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5348)
    在com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:214)
    在com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:89)
    在sun.reflect.NativeMethodAccessorImpl.invoke0(本地方法)
    在sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    在java.lang.reflect.Method.invoke(Method.java:601)
    在com.sun.jersey.spi.container.JavaMethodInvokerFactory $ 1.invoke(JavaMethodInvokerFactory.java:60)
    在com.sun.jersey.server.impl.model.method.dispatch.Abs​​tractResourceMethodDispatchProvider $ ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205)
    在com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
    在com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
    在com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
    在com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
    在com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
    在com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469)
    在com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400)
    在com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)
    在com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)
    在com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)
    在com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
    在com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
    在javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
    在org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
    在org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
    在org.apache.catalina.core.StandardContextValve .__ invoke(StandardContextValve.java:175)
    在org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
    在org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    在org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    在org.apache.catalina.core.StandardHostValve .__ invoke(StandardHostValve.java:161)
    在org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
    在org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
    在org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
    在com.sun.enterprise.v3.services.impl.ContainerMapper $ AdapterCallable.call(ContainerMapper.java:317)
    在com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
    在com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
    在com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
    在com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
    在com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
    在com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    在com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    在com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    在com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    在com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    在com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    在com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    在com.sun.grizzly.util.AbstractThreadPool $ Worker.doWork(AbstractThreadPool.java:532)
    在com.sun.grizzly.util.AbstractThreadPool $ Worker.run(AbstractThreadPool.java:513)
    在java.lang.Thread.run(Thread.java:722)|#]

您可以使用自定义的TypeAdapter手动取消所有的操作。 沿着这些线路的东西:

 /** * This TypeAdapter unproxies Hibernate proxied objects, and serializes them * through the registered (or default) TypeAdapter of the base class. */ public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> { public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @Override @SuppressWarnings("unchecked") public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null); } }; private final Gson context; private HibernateProxyTypeAdapter(Gson context) { this.context = context; } @Override public HibernateProxy read(JsonReader in) throws IOException { throw new UnsupportedOperationException("Not supported"); } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void write(JsonWriter out, HibernateProxy value) throws IOException { if (value == null) { out.nullValue(); return; } // Retrieve the original (not proxy) class Class<?> baseType = Hibernate.getClass(value); // Get the TypeAdapter of the original class, to delegate the serialization TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType)); // Get a filled instance of the original class Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer() .getImplementation(); // Serialize the value delegate.write(out, unproxiedValue); } } 

要使用它,你必须先注册它:

 GsonBuilder b = new GsonBuilder(); ... b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY); ... Gson gson = b.create(); 

请注意,这将recursion地初始化您在对象层次结构中的每个代理; 因为无论如何你必须序列化整个数据,你应该做到这一点。

这个怎么用?

GSON包含许多TypeAdapterFactory实现,用于各种types(基本types,常见types如StringDate ,列表,数组…)。 每个工厂都会询问它是否能够序列化一个特定的Javatypes(要create的参数是一个TypeToken而不是一个Class ,以获取关于genericstypes的可能的信息,哪些Class没有)。 如果工厂能够序列化/反序列化一个types,它将返回一个TypeAdapter实例; 否则它会返回null

HibernateProxyTypeAdapter.FACTORYvalidationtypes是否实现HibernateProxy ; 在这种情况下,它返回一个HibernateProxyTypeAdapter的实例用于序列化。 write方法在实际的对象被序列化时调用; 该适配器将提取底层对象的原始types,并为原始types(通常为ReflectiveTypeAdapter TypeAdapter请求标准TypeAdapter

然后它检索原始类的一个实例,而不是直接使用代理。 这是必要的,因为ReflectiveTypeAdapter 直接访问字段 ,而不是使用getter; 访问代理对象的字段不起作用,是一个经典的Hibernate陷阱 。

作为一种可能的性能改进,应该在create方法中获取委托typesTypeAdapter 。 我发现在代理Class上调用getSuperclass()似乎会产生原始的基类。 代码可以变成:

 public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() { @Override @SuppressWarnings("unchecked") public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter((TypeAdapter)gson.getAdapter(TypeToken.get(type.getRawType().getSuperclass()))) : null); } }; private final TypeAdapter<Object> delegate; private HibernateProxyTypeAdapter(TypeAdapter<Object> delegate) { this.delegate = delegate; } @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void write(JsonWriter out, HibernateProxy value) throws IOException { if (value == null) { out.nullValue(); return; } delegate.write(out, ((HibernateProxy) value).getHibernateLazyInitializer() .getImplementation()); } 

在通常情况下,您不希望域对象通过服务作为XML / JSON公开,因此通常需要创buildDTO,因为您的实体不适合消费者的需求。 而且即使现在这样做了,在内部重构数据库之后,它明天也不适合。 所以我的build议是现在创buildDTO,如果你遇到这样的麻烦。 顺便说一句,你可以通过使用结果转换器或者创build视图并将Hibernate实体映射到这些视图上来创build那些甚至在Hibernate级别上的DTO。

另一个窍门是使用推土机 ,以便将需要的字段复制到其他类(实际上是相同的类,但没有代理)。

注意:你使用Gson来访问你的字段 ,而不是访问者,这使得Hibernate代理无法工作,因为它会尝试访问总是为null的代理本身的字段。

看到你提到这个错误依然存在,那么这个问题可能不是那么多的Hibernate,但可能在GSON实现上。 我觉得在创buildJSON的时候你需要一个Type,不知道它是否被注册了,但是可能是这样的:

 public String autosToJson(Auto autos) { GsonBuilder gsonBuilder = new GsonBuilder(); Gson gson = gsonBuilder.registerTypeAdapter(Auto.class, new AutoAdapter()).create(); return gson.toJson(autos); } 

然后创build一个AdapterClass,例如:

 public class AutoAdapter implements JsonSerializer<Auto> { @Override public JsonElement serialize(Auto auto, Type type, JsonSerializationContext jsc) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("auto_id", auto.getId()); jsonObject.addProperty("auto_name", auto.getAutoName()); jsonObject.addProperty("auto__manufacture_date", auto.getManufactureDate().toString()); return jsonObject; } } 

是的,你可以随时unproxy,如果它有一个HibernateProxy(这将不会序列化)它将会消失,取而代之的是实际的底层实现,或者它会离开类,并给你一个实现。 我认为你的解决scheme应该正常工作。 请注意,我并没有真正使用Hibernate,但它对我来说确实有意义。

另一方面,你可能会更信任Hibernate,但更简单的方法可能是:

 Hibernate.getClass(obj); 

这个解决scheme不应该给你实现/初始化的类,只是类,或者应该提供的function:

 HibernateProxyHelper.getClassWithoutInitializingProxy(superClass) 

我相信后者可能会返回超类,所以你可以从Hibernate.getClass(obj)开始;

也:

 public static <T> T initializeAndUnproxy(T entity) { if (entity == null) { throw new NullPointerException("Entity passed for initialization is null"); } Hibernate.initialize(entity); if (entity instanceof HibernateProxy) { entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer() .getImplementation(); } return entity; } 

上面的代码是借用的: 将Hibernate代理转换为实际对象,其中的variables名称可能更好,因为它们并不意味着实体始终是代理。 它也会抛出一个exception来警告你,但这是由你决定是否你想要的例外与否。

当然,你也可以摆脱懒惰的负载,但我不认为这是最好的解决scheme。

当我碰到这个post的时候,我遇到了这个问题,这个post指出了我的处境是正确的。 我意识到我不需要序列化整个实体,特别是因为我已经标记了一些字段被延迟加载。 所以我试图find一种方法来跳过这些领域和ExclusionStrategy是神奇的。 这似乎已经解决了我的问题

 public class ExcludeProxiedFields implements ExclusionStrategy{ @Override public boolean shouldSkipField(FieldAttributes fa) { return fa.getAnnotation(ManyToOne.class) != null || fa.getAnnotation(OneToOne.class) != null || fa.getAnnotation(ManyToMany.class) != null || fa.getAnnotation(OneToMany.class) != null ; } @Override public boolean shouldSkipClass(Class<?> type) { return false; } } 

然后我将这个类应用到GsonBuilder中,如下所示:

 Gson gson = new GsonBuilder().setExclusionStrategies(new ExcludeProxiedFields()).create();