使用Jackson序列化Collection时的LazyInitializationException

时间:2015-02-03 09:21:16

标签: java hibernate jackson resteasy lazy-initialization

我正在使用AngularJs和Rest服务(RestEasy)构建应用程序,我需要在其中显示产品列表。当用户点击其中一个产品时加载列表后,将加载该产品的详细信息和图像。

当我显示所有产品的列表时,我不想加载与产品相关的集合(即图像),但仅在我需要其中一个产品的详细信息时才出于性能原因,所以我&#39 ; m使用Lazy load。

Hibernate做得很好我可以看到,最初我只加载了一些属性但不加载集合,但是在休息服务中,当杰克逊试图访问该集合来序列化它时,我得到了这个着名的例外:

Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.domainmodel.Product.Images

当然,此时不再打开Session,并且已经提交了数据库事务。我不想保持会话开放。因此,在网上搜索我看到我们可以告诉杰克逊不要使用注释@JsonIgnore序列化一个属性。 这个注释必须放在Entity(POJO)的属性或getter中,所以基本上,如果我告诉Jackson在序列化为JSON时不要序列化这个集合,它会暂时解决问题但是一旦我需要加载产品该集合因为用户点击了它,它不会序列化该集合,所以我稍后会遇到这个问题。所以我不认为这对我来说是一个解决方案。关于如何解决这种情况的任何想法,建议?我在这里发布实体的代码和其他服务。提前谢谢!!

@Entity
@Table(name = "PRODUCTS")
public class Product implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;

@Column(name = "PATH_TO_IMAGE", nullable = false, insertable = true, updatable = true)
private String pathToImage;

@Column(name="DESCRIPTION",nullable=true, insertable = true, updatable = true)
private String description;

@ManyToOne(optional = false)
@JoinColumn(name = "XID_PRODUCT_TYPE", nullable = false, insertable = false, updatable = false)
private ProductType productType;


@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
        name = "PRODUCT_IMAGE",
        joinColumns = {@JoinColumn(name = "XID_PRODUCT", referencedColumnName = "ID")},
        inverseJoinColumns = {@JoinColumn(name = "XID_IMAGE", referencedColumnName = "ID")})
private List<Image> images;

... getters and setters 
}

然后是其他服务的代码:

@Path("/products")
public class ProductsService extends CommonService{

private static final Logger log =  Logger.getLogger(ProductsService.class);


@EJB
private ProductsServiceHandler handler;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllProducts(@Context Request request, @Context HttpHeaders httpHeaders)
{
    try
    {
        List<Product> products=handler.findAllProducts();
        return Response.ok(products).header(ALLOW_ORIGIN_HEADER, "*").build();
    }
    catch(Exception e){return Response.status(Status.INTERNAL_SERVER_ERROR).build();}
}

}

按要求完成stackstrace:

 2015-02-03 00:35:41,085 ERROR [io.undertow.request] (default task-9) UT005023: Exception handling request to /arenaclub/rest/products: org.jboss.resteasy.spi.UnhandledException: Response is committed, can't handle exception
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:148) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:432) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:376) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:179) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:220) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [resteasy-jaxrs-3.0.8.Final.jar:]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:113) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:56) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:45) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:61) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:58) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:70) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.security.handlers.SecurityInitialHandler.handleRequest(SecurityInitialHandler.java:76) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:25) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:240) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:73) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:146) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:177) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:727) [undertow-core-1.0.15.Final.jar:1.0.15.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [rt.jar:1.8.0_25]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [rt.jar:1.8.0_25]
at java.lang.Thread.run(Thread.java:745) [rt.jar:1.8.0_25]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.fgonzalez.domainmodel.Product.images, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.fgonzalez.domainmodel.Product["images"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:197) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:187) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:652) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:152) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:100) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:21) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase.serialize(AsArraySerializerBase.java:183) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:114) [jackson-databind-2.3.2.jar:2.3.2]
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:610) [jackson-databind-2.3.2.jar:2.3.2]
at org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider.writeTo(ResteasyJackson2Provider.java:186) [resteasy-jackson2-provider-3.0.8.Final.jar:]
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.writeTo(AbstractWriterInterceptorContext.java:129) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.interception.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:62) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:118) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.security.doseta.DigitalSigningInterceptor.aroundWriteTo(DigitalSigningInterceptor.java:143) [resteasy-crypto-3.0.8.Final.jar:]
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:122) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.plugins.interceptors.encoding.GZIPEncodingInterceptor.aroundWriteTo(GZIPEncodingInterceptor.java:100) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.interception.AbstractWriterInterceptorContext.proceed(AbstractWriterInterceptorContext.java:122) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:99) [resteasy-jaxrs-3.0.8.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:427) [resteasy-jaxrs-3.0.8.Final.jar:]
... 32 more

4 个答案:

答案 0 :(得分:4)

你可以做几件事。

  1. 最快的方式 - 在返回结果之前循环浏览products并在每个产品上调用setImages(null)
  2. Product注册两个自定义JSON序列化程序,一个用于序列化图像,另一个用于序列化图像,并根据所调用的服务选择序列化程序。问题是我不确定是否可以使用RestEasy完成,但请查看this thread以获取一些提示
  3. 不要从REST服务返回您的实体bean,而是使用DTO,这样您就可以轻松控制返回的内容和格式。另一个重要的优点是,您可以将REST层与实体bean分离,从而可以自由地更改实体,而无需担心更改REST用户期望的格式。这将是首选方案。

答案 1 :(得分:4)

您可以考虑两种可能的选择:

  1. 为您的服务层使用单独的数据传输对象(DTO),因此您将在实体 - &gt;期间管理获取所需信息。 DTO 转换。这种方法的缺点 - 您需要编写和维护这些转换器。另一方面,您可以充分灵活地将任何实体组合到您的服务响应中。首选方案。
  2. 创建单独的轻量级实体(例如&#34; ProductInfo&#34;)以用于&#34;产品&#34;呼叫。将多个实体映射到一个DB表不应该有任何问题。

答案 2 :(得分:1)

杰克逊项目的人们开发了一个很好的模块来解决这个问题!

在github上查看: https://github.com/FasterXML/jackson-datatype-hibernate

答案 3 :(得分:0)

您可以在JPA会话中运行Jackson,因此延迟加载将起作用。