LazyInitializationException при сериализации коллекции с Джексоном

1

Я создаю приложение, используя службы AngularJs и Rest (RestEasy), где мне нужно отображать список продуктов. Когда список загружается, когда пользователь нажимает на один из продуктов, загружаются детали и изображения этого продукта.

Я не хочу загружать коллекции, связанные с продуктами (например, изображения), когда я показываю список всех продуктов, но только когда мне нужны детали одного из них по соображениям производительности, поэтому я использую Lazy load.

Hibernate все в порядке, и я вижу, что изначально я загружаю только некоторые атрибуты, но не коллекции, однако в службе отдыха, когда Джексон пытается получить доступ к коллекции для ее сериализации, я получаю это знаменитое исключение:

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

Конечно, в этот момент сеанс не открывается, и транзакция базы данных была совершена. Я не хочу, чтобы сессия открывалась. Таким образом, поиск в сети я увидел, что мы можем сказать, что Джексон не сериализует свойство, используя аннотацию @JsonIgnore. Эта аннотация должна быть помещена в свойство или получатель Entity (POJO), поэтому в основном, если я скажу 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();}
}

}

Полный стек из запрошенного:

 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
  • 0
    Похоже, у вас проблема с сеансом: `не удалось инициализировать прокси - нет сеанса`
Теги:
hibernate
jackson
resteasy
lazy-initialization

4 ответа

4
Лучший ответ

Вы можете рассмотреть два возможных варианта:

  1. Используйте отдельные объекты передачи данных (DTO) для вашего уровня обслуживания, чтобы вы могли получать необходимую информацию во время преобразования Entity → DTO. Недостаток этого подхода - вам нужно писать и поддерживать эти преобразователи. С другой стороны, у вас есть полная гибкость объединения любых объектов в ваш ответ службы. Предпочтительный вариант.
  2. Создайте отдельный легкий объект (например, "ProductInfo"), который будет использоваться для вызова "продуктов". Не должно быть никаких проблем с отображением нескольких объектов в одну таблицу БД.
3

Есть несколько вещей, которые вы могли бы сделать.

  1. Самый быстрый способ - setImages(null) products и вызвать setImages(null) для каждого продукта до возвращения результата
  2. Зарегистрируйте два настраиваемых сериализатора JSON для Product, один из которых будет сериализовать изображения, а другой - нет, и выберите сериализатор на основе вызванного сервиса. Проблема в том, что я не уверен, что это можно сделать с помощью RestEasy, но посмотрите на эту тему для некоторых советов
  3. Не возвращайте свои сущности из службы REST, вместо этого используйте DTO, чтобы вы могли легко контролировать то, что вы возвращаете, и в каком формате. Еще один большой плюс заключается в том, что вы отделяете слой REST от сущностей, что дает вам свободу менять объекты, не беспокоясь об изменении формата, который ожидают пользователи REST. Это был бы предпочтительный вариант.
  • 0
    Версия setImages(null) спасла мне жизнь в сочетании с вариантом 3.!
1

Люди в проекте Джексона разработали хороший модуль для решения этой проблемы!

Проверьте это на github по адресу: https://github.com/FasterXML/jackson-datatype-hibernate

0

Вы можете запустить Джексона внутри сессии JPA, поэтому ленивая загрузка будет работать.

  • 0
    Как бы Вы это сделали?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню