mvc example java spring hibernate jsp spring-mvc

java - example - Cómo resolver la excepción de Hibernate "no se pudo inicializar perezosamente una colección de roles"



spring mvc hibernate crud example eclipse (25)

El origen de tu problema:

Por defecto, hibernate carga las colecciones (relaciones) de manera perezosa, lo que significa que cuando use la collection en su código (aquí el campo de comments en la clase de Topic ) la hibernación obtiene eso de la base de datos, ahora el problema es que obtiene la colección en su controlador (donde la sesión 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<Comment> commentList = topicById.getComments();

Está recibiendo la colección de "comentarios" (topic.getComments ()) en su controlador (donde la JPA session ha finalizado) y eso causa la excepción. Además, si tiene la colección de comments en su archivo jsp de esta manera (en lugar de hacerlo en su controlador):

<c:forEach items="topic.comments" var="item"> //some code </c:forEach>

Aún tendría la misma excepción por la misma razón.

Resolviendo el problema:

Debido a que solo puede tener dos colecciones con FetchType.Eager (colección ansiosamente FetchType.Eager ) en una clase de Entidad y debido a que la carga diferida es más eficiente que la carga con entusiasmo, creo que esta forma de resolver su problema es mejor que simplemente cambiar el FetchType a ansioso :

Si desea tener la recopilación perezosa inicializada y también hacer que esto funcione, es mejor agregar este fragmento de código a su web.xml :

<filter> <filter-name>SpringOpenEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>SpringOpenEntityManagerInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

Lo que hace este código es que aumentará 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." De esta manera, la sesión JPA se abrirá un poco más y, por lo tanto, puede cargar colecciones en sus archivos jsp y clases de controladores.

Tengo este problema:

org.hibernate.LazyInitializationException: no se pudo inicializar perezosamente una colección de roles: mvc3.model.Topic.comments, ninguna sesión o sesión fue cerrada

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<Comment> comments = new LinkedHashSet<Comment>(); ... public Collection<Comment> getComments() { return comments; } }

El controlador, que llama modelo se parece a lo siguiente:

@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<Comment> 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 li l lo siguiente:

<%@page import="com.epam.mvc3.helpers.Utils"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ page session="false" %> <html> <head> <title>View Topic</title> </head> <body> <ul> <c:forEach items="${commentList}" var="item"> <jsp:useBean id="item" type="mvc3.model.Comment"/> <li>${item.getText()}</li> </c:forEach> </ul> </body> </html>

La excepción aumenta, al ver jsp. En la línea con c: forEach loop


Al usar la anotación hibernate @Transactional , si obtiene un objeto de la base de datos con atributos de búsqueda @Transactional , puede obtenerlos simplemente obteniendo estos atributos de la siguiente manera:

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

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


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

select t from Topic t left join fetch t.comments

SIEMPRE debes evitar los siguientes patrones:

Por lo tanto, asegúrese de que sus asociaciones FetchType.LAZY estén inicializadas en el momento de la consulta o dentro del alcance @Transactional original utilizando Hibernate.initialize para colecciones secundarias.


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

@PersistenceContext(type = PersistenceContextType.EXTENDED)


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 sintaxis de JOIN FETCH en su JPQL para obtener explícitamente la colección secundaria. Esto es algo así como la recuperación de EAGER.

(3) Utilice OpenSessionInViewFilter

LazyInitializationException a menudo se produce en la capa de vista. Si usa Spring Framework, puede usar OpenSessionInViewFilter. Sin embargo, no te sugiero que lo hagas. Puede conducir a un problema de rendimiento si no se utiliza correctamente.


El motivo es que cuando se utiliza la carga diferida, la sesión se cierra.

Hay dos soluciones.

  1. No utilice la carga perezosa.

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

  2. Utilice la carga perezosa.

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

    y agregue el OpenSessionInViewFilter filter en su web.xml

Detalle ver mi POST .


El problema se debe 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. Haga 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 en el 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. Utilice ''eager'' en lugar de ''perezoso'' . Ahora no está usando ''perezoso'' ... no es una solución real, si quiere usar perezoso, funciona como una solución temporal (muy temporal).

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

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


En mi cae, tuve el mapeo b / w A y B como

A tiene

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

en la capa DAO, el método debe anotarse con @Transactional si no ha anotado la asignación con Fetch Type - Eager


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

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

Debido a que se separó de la base de datos y Hibernate ya no recuperó la lista del campo cuando fue necesario. Así que lo inicializo antes de separarlo:

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


Falta la anotación transaccional en el controlador

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


Fue el problema que enfrenté recientemente, que resolví utilizando

<f:attribute name="collectionType" value="java.util.ArrayList" />

Una descripción más detallada here y esto salvó mi día.


Hola a todos los que publican bastante tarde, espero que ayude a otros, Agradeciendo de antemano a @GMK por esta publicación Hibernate.initialize

cuando perezoso = "verdadero"

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

ahora si accedo a ''set'' después de cerrar la sesión, se produce una excepción.

Mi solución :

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

ahora puedo acceder a ''set'' incluso después de cerrar la sesión de Hibernate.


La razón es que está intentando obtener la lista de comentarios en su controlador después de cerrar la sesión dentro del servicio.

topicById.getComments();

Anteriormente cargará la lista de comentarios solo si su sesión de hibernación está activa, lo que supongo que cerró en su servicio.

Por lo tanto, debe obtener la lista de comentarios antes de cerrar la sesión.


Los comments colección en el Topic su clase modelo están cargados perezosamente, que es el comportamiento predeterminado si no lo fetch = FetchType.EAGER específicamente con fetch = FetchType.EAGER .

Es muy probable que su servicio findTopicByID esté utilizando una sesión de Hibernate sin estado. Una sesión sin estado no tiene el caché de primer nivel, es decir, no hay contexto de persistencia. Más adelante, cuando intente iterar 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. Anote los comments con fetch = FetchType.EAGER

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

  2. Si aún desea que los comentarios se carguen de forma perezosa, use las sesiones con estado de Hibernate , de modo que pueda obtener comentarios más adelante a pedido.


Otra forma de hacer la cosa, puede usar TransactionTemplate para envolver alrededor de la búsqueda perezosa. Me gusta

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


Para aquellos que trabajan con Criteria , encontré que

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

Hice todo lo que necesitaba haber hecho.

El modo de recuperación inicial para colecciones se establece en FetchMode.LAZY para proporcionar rendimiento, pero cuando necesito los datos, solo agrego esa línea y disfruto de los objetos completamente poblados.


Para cargar de forma perezosa una colección debe haber una sesión activa. En una aplicación web hay dos formas de hacer esto. Puede usar el patrón Abrir sesión en la vista , donde usa un interceptor para abrir la sesión al principio de la solicitud y cerrarla al final. El riesgo es que tenga que tener un manejo de excepciones sólido o podría bloquear todas las sesiones y la 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 meter 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 recibe 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 deberá volver a adjuntar 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.


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

<tx:annotation-driven transaction-manager="myTxManager" />

en el archivo de contexto de aplicación.

La anotación @Transactional sobre un método no fue tomada en cuenta.

Espero que la respuesta ayude a alguien.


Resolví usando Lista en lugar de Establecer:

private List<Categories> children = new ArrayList<Categories>();


Sé que es una vieja pregunta pero quiero ayudar. Puede poner la anotación transaccional en el método de servicio que necesita, 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 here

Sobre las otras soluciones:

fetch = FetchType.EAGER

no es una buena práctica, se debe usar SOLO si es necesario.

Hibernate.initialize(topics.getComments());

El inicializador de hibernación une sus clases a la tecnología de hibernación. Si tu objetivo es ser flexible no es una buena manera de hacerlo.

Espero eso ayude


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

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


Si sabe que querrá ver todos los Comment cada vez que recupere un Topic , cambie su asignación de campo para comments a:

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

Las colecciones están cargadas de forma predeterminada, mire this si desea saber más.


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


su lista es de carga lenta, por lo que la lista no se cargó. llamar para entrar en la lista no es suficiente. utilizar en Hibernate.initialize para iniciar la lista. Si dosnt funciona, ejecute el elemento de lista y llame a Hibernate.initialize para cada uno. esto debe ser antes de regresar desde el alcance de la transacción. mira this post
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


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

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