Cómo resolver el error “Inicialización lenta de una colección de roles” Excepción de Hibernate

Tengo este problema:

org.hibernate.LazyInitializationException: no se pudo inicializar de forma lenta una colección de roles: mvc3.model.Topic.comments, no se cerró ninguna sesión o sesión

Aquí está el modelo:

@Entity @Table(name = "T_TOPIC") public class Topic { @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; @ManyToOne @JoinColumn(name="USER_ID") private User author; @Enumerated(EnumType.STRING) private Tag topicTag; private String name; private String text; @OneToMany(mappedBy = "topic", cascade = CascadeType.ALL) private Collection comments = new LinkedHashSet(); ... public Collection getComments() { return comments; } } 

El controlador, que llama modelo se ve así:

 @Controller @RequestMapping(value = "/topic") public class TopicController { @Autowired private TopicService service; private static final Logger logger = LoggerFactory.getLogger(TopicController.class); @RequestMapping(value = "/details/{topicId}", method = RequestMethod.GET) public ModelAndView details(@PathVariable(value="topicId") int id) { Topic topicById = service.findTopicByID(id); Collection commentList = topicById.getComments(); Hashtable modelData = new Hashtable(); modelData.put("topic", topicById); modelData.put("commentList", commentList); return new ModelAndView("/topic/details", modelData); } } 

La página jsp parece li lo siguiente:

       View Topic   
  • ${item.getText()}

Se produce una excepción al visualizar jsp. En la línea con c: para cada bucle

Si sabes que querrás ver todos los Comment cada vez que recuperes un Topic , cambia tu mapeo de campo para comments a:

 @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection comments = new LinkedHashSet(); 

Las colecciones se cargan de manera diferida por defecto, eche un vistazo a esto si quiere saber más.

Desde mi experiencia, tengo los siguientes métodos para resolver la famosa LazyInitializationException:

(1) Use Hibernate.initialize

 Hibernate.initialize(topics.getComments()); 

(2) Utilice JOIN FETCH

Puede usar la syntax JOIN FETCH en su JPQL para extraer explícitamente la colección secundaria. Esto es de alguna manera como buscar EAGER.

(3) Use OpenSessionInViewFilter

LazyInitializationException a menudo ocurre en la capa de vista. Si usa Spring framework, puede usar OpenSessionInViewFilter. Sin embargo, no te sugiero que lo hagas. Puede provocar problemas de rendimiento si no se usa correctamente.

El origen de tu problema:

Por defecto hibernate carga las colecciones (relaciones) de forma perezosa, lo que significa que cuando usas la collection en tu código (aquí el campo de comments en clase Topic ) el hibernate obtiene eso de la base de datos, ahora el problema es que estás obteniendo la colección en tu controlador (donde la sesión de JPA está cerrada). Esta es la línea de código que causa la excepción (donde está cargando la colección de comments ):

  Collection commentList = topicById.getComments(); 

Obtendrá la colección de “comentarios” (topic.getComments ()) en su controlador (donde finalizó la JPA session ) y eso causa la excepción. Además, si tuviera la colección de comments en su archivo jsp de esta manera (en lugar de obtenerlo en su controlador):

  //some code  

Aún tendrías la misma excepción por el mismo motivo.

Resolviendo el problema:

Como solo puede tener dos colecciones con FetchType.Eager (colección FetchType.Eager con impaciencia) en una clase Entity y porque la carga diferida es más eficiente que cargar con impaciencia, creo que esta forma de resolver su problema es mejor que simplemente cambiar FetchType a impaciente. :

Si desea que la colección tenga una inicialización lenta y también haga que esto funcione, es mejor agregar este fragmento de código a su web.xml :

  SpringOpenEntityManagerInViewFilter org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter   SpringOpenEntityManagerInViewFilter /*  

Lo que hace este código es que boostá la duración de su JPA session o, como dice la documentación, se usa "to allow for lazy loading in web views despite the original transactions already being completed." así que de esta manera la sesión de JPA se abrirá un poco más y por eso puede cargar colecciones de forma perezosa en sus archivos jsp y clases de controladores.

Sé que es una vieja pregunta, pero quiero ayudar. Puedes poner la anotación transaccional en el método de servicio que necesites, en este caso findTopicByID (id) debería tener

 @Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class) 

más información sobre esta anotación se puede encontrar aquí

Sobre las otras soluciones:

 fetch = FetchType.EAGER 

no es una buena práctica, debe usarse ÚNICAMENTE si es necesario.

 Hibernate.initialize(topics.getComments()); 

El inicializador de hibernación vincula sus clases a la tecnología de hibernación. Si pretendes ser flexible no es una buena forma de hacerlo.

Espero eso ayude

La razón es que cuando usa carga diferida, la sesión se cierra.

Hay dos soluciones.

  1. No use la carga lenta.

    Establezca lazy=false en XML o Set @OneToMany(fetch = FetchType.EAGER) En la anotación.

  2. Usa carga lenta

    Establezca lazy=true en XML o Set @OneToMany(fetch = FetchType.LAZY) En la anotación.

    y agrega el OpenSessionInViewFilter filter en tu web.xml

Detalle Ver mi POST .

Para cargar una colección de forma perezosa, debe haber una sesión activa. En una aplicación web, hay dos formas de hacerlo. Puede usar el patrón Abrir sesión en vista , donde usa un interceptor para abrir la sesión al principio de la solicitud y cerrarla al final. El riesgo es que debe tener un manejo de excepciones sólido o puede enlazar todas sus sesiones y su aplicación podría bloquearse.

La otra forma de manejar esto es recolectar todos los datos que necesita en su controlador, cerrar su sesión y luego almacenar los datos en su modelo. Personalmente prefiero este enfoque, ya que parece un poco más cercano al espíritu del patrón MVC. Además, si obtiene un error de la base de datos de esta manera, puede manejarlo mucho mejor que si ocurriera en su procesador de vistas. Tu amigo en este escenario es Hibernate.initialize (myTopic.getComments ()). También tendrá que volver a conectar el objeto a la sesión, ya que está creando una nueva transacción con cada solicitud. Use session.lock (myTopic, LockMode.NONE) para eso.

El problema se produce al acceder a un atributo con la sesión de hibernación cerrada. No tiene una transacción de hibernación en el controlador.

Soluciones posibles:

  1. Haz toda esta lógica, en la capa de servicio , (con @Transactional), no en el controlador. Debe haber el lugar correcto para hacer esto, es parte de la lógica de la aplicación, no del controlador (en este caso, una interfaz para cargar el modelo). Todas las operaciones en la capa de servicio deben ser transaccionales. es decir: Mueva esta línea al método TopicService.findTopicByID:

    Colección commentList = topicById.getComments ();

  2. Use ‘ansioso’ en lugar de ‘perezoso’ . Ahora no estás usando ‘perezoso’ … no es una solución real, si quieres usar perezoso, funciona como una solución temporal (muy temporal).

  3. usa @Transactional en el controlador . No debe usarse aquí, está mezclando capa de servicio con presentación, no es un buen diseño.
  4. use OpenSessionInViewFilter , muchas desventajas reportadas, posible inestabilidad.

En general, la mejor solución es el 1.

 @Controller @RequestMapping(value = "/topic") @Transactional 

@Transactional este problema agregando @Transactional , creo que esto puede hacer que la sesión se abra

Si está tratando de tener una relación entre una entidad y una Colección o una Lista de objetos Java (por ejemplo, Tipo largo), le gustaría algo como esto:

 @ElementCollection(fetch = FetchType.EAGER) public List ids; 

Descubrí que declarar @PersistenceContext como EXTENDED también resuelve este problema:

 @PersistenceContext(type = PersistenceContextType.EXTENDED) 

Falta la anotación @Transactional en el controlador

 @Controller @RequestMapping("/") @Transactional public class UserController { } 

fue el problema que enfrenté recientemente que resolví con el uso

  

descripción más detallada aquí y esto me salvó el día.

su lista es de carga lenta, por lo que la lista no se cargó. llamar para estar en la lista no es suficiente. utilizar en Hibernate.initialize para iniciar la lista. Si el trabajo de dosnt se ejecuta en el elemento list y llame a Hibernate.initialize para cada uno. esto debe ser antes de regresar del scope de la transacción. mira esta publicación.
buscar –

 Node n = // .. get the node Hibernate.initialize(n); // initializes 'parent' similar to getParent. Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 

Para resolver el problema en mi caso solo faltaba esta línea

  

en el archivo de contexto de la aplicación.

La anotación @Transactional sobre un método no se tuvo en cuenta.

Espero que la respuesta ayude a alguien

Para aquellos que trabajan con Criteria , encontré que

 criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER); 

hizo todo lo que necesitaba había hecho.

El modo de búsqueda inicial para las colecciones se establece en FetchMode.LAZY para proporcionar rendimiento, pero cuando necesito los datos, solo agrego esa línea y disfruto de los objetos totalmente poblados.

En mi caso, el siguiente código fue un problema:

 entityManager.detach(topicById); topicById.getComments() // exception thrown 

Porque se separó de la base de datos e Hibernate ya no recuperó la lista del campo cuando era necesario. Entonces lo inicializo antes de separarlo:

 Hibernate.initialize(topicById.getComments()); entityManager.detach(topicById); topicById.getComments() // works like a charm 

Como expliqué en este artículo , la mejor manera de manejar la LazyInitializationException es recuperarla en el momento de la consulta, como esta:

 select t from Topic t left join fetch t.comments 

SIEMPRE debe evitar los siguientes antipatrones:

  • haciendo las asociaciones EAGER
  • usando OSIV (Open Session in View)
  • habilitando la propiedad de configuración hibernate.enable_lazy_load_no_trans Hibernate

Por lo tanto, asegúrese de que sus asociaciones FetchType.LAZY se inicialicen en el momento de la consulta o dentro del scope @Transactional original usando Hibernate.initialize para colecciones secundarias.

La razón es que está tratando de obtener el commentList en su controlador después de cerrar la sesión dentro del servicio.

 topicById.getComments(); 

Arriba cargará el commentList solo si su sesión de hibernación está activa, lo que supongo que cerró en su servicio.

Entonces, debe obtener el commentList antes de cerrar la sesión.

En mi cae, tenía el mapeo b / w A y B como

A tiene

 @OneToMany(mappedBy = "a", cascade = CascadeType.ALL) Set bs; 

en la capa DAO, el método debe ser anotado con @Transactional si no ha anotado la asignación con Tipo de extracción – Ansioso

Los comments colección en el Topic su clase de modelo se cargan de forma lenta, que es el comportamiento predeterminado si no se anota con fetch = FetchType.EAGER específicamente.

Es muy probable que su servicio findTopicByID esté utilizando una sesión de Hibernate sin estado. Una sesión sin estado no tiene el primer nivel de caché, es decir, no tiene contexto de persistencia. Más adelante, cuando intentes iterar los comments , Hibernate lanzará una excepción.

 org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closed 

La solución puede ser:

  1. Anotar comments con fetch = FetchType.EAGER

     @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection comments = new LinkedHashSet(); 
  2. Si aún desea que los comentarios se carguen de forma diferida, use las sesiones con estado de Hibernate , de modo que pueda obtener comentarios más adelante a pedido.

Hola, todos publicando bastante tarde espero que ayude a los demás, Agradeciendo de antemano a @GMK por esta publicación Hibernate.initialize (object)

cuando Lazy = “true”

 Set set=null; hibernateSession.open set=hibernateSession.getMyObjects(); hibernateSession.close(); 

ahora si accedo a ‘establecer’ después de cerrar la sesión arroja una excepción.

Mi solución :

 Set set=new HashSet(); hibernateSession.open set.addAll(hibernateSession.getMyObjects()); hibernateSession.close(); 

ahora puedo acceder a ‘establecer’ incluso después de cerrar Hibernate Session.

Al utilizar la anotación hibernate @Transactional , si obtienes un objeto de la base de datos con atributos vagos, simplemente puedes obtenerlos obteniendo estos atributos de esta manera:

 @Transactional public void checkTicketSalePresence(UUID ticketUuid, UUID saleUuid) { Optional savedTicketOpt = ticketRepository.findById(ticketUuid); savedTicketOpt.ifPresent(ticket -> { Optional saleOpt = ticket.getSales().stream().filter(sale -> sale.getUuid() == saleUuid).findFirst(); assertThat(saleOpt).isPresent(); }); } 

Aquí, en una transacción administrada por proxy de Hibernate, el hecho de llamar a ticket.getSales() hace otra consulta para obtener ventas porque usted lo solicitó explícitamente.

Una de las mejores soluciones es agregar lo siguiente en su archivo application.properties: spring.jpa.properties.hibernate.enable_lazy_load_no_trans = true

Otra forma de hacerlo, puede usar TransactionTemplate para ajustar la recuperación perezosa. Me gusta

 Collection commentList = this.transactionTemplate.execute (status -> topicById.getComments()); 

agregue esto a su persistence.xml

  

resolví usar List en lugar de Set:

 private List children = new ArrayList();