Cómo resolver la excepción de Hibernación "no se pudo inicializar de forma diferida una colección de funciones"
Esta es la excepción:
org.hibernate.LazyInitializationException: no se pudo inicializar de forma diferida una colección de roles: mvc3.model.Topic.comments, no se cerró ninguna sesión o se cerró la 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<Comment> comments = new LinkedHashSet<Comment>();
...
public Collection<Comment> getComments() {
return comments;
}
}
El controlador, que llama al modelo, tiene el siguiente aspecto:
@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 tiene el siguiente aspecto:
<%@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 se produce al visualizar el archivo jsp. En la línea con c:forEach bucle
Si sabe que querrá ver todos Comment
los correos electrónicos cada vez que recupere un Topic
, cambie su asignación de campos comments
a:
@OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
Las colecciones se cargan de forma diferida de forma predeterminada; eche un vistazo a esto si desea saber más.
Desde mi experiencia, tengo los siguientes métodos para resolver los famosos LazyInitializationException
:
(1) Utilice Hibernate.initialize
Hibernate.initialize(topics.getComments());
(2) Utilice UNIRSE Y BUSCAR
Puede utilizar la JOIN FETCH
sintaxis de su JPQL para recuperar explícitamente la colección secundaria. Esto es de alguna manera como EAGER
buscar.
(3) Utilice 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 utiliza correctamente.
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)
Puede encontrar más información sobre esta anotación aquí.
Acerca de las otras soluciones:
fetch = FetchType.EAGER
No es una buena práctica, debe usarse SÓLO si es necesario.
Hibernate.initialize(topics.getComments());
El inicializador de hibernación vincula sus clases a la tecnología de hibernación. Si su objetivo es ser flexible, no es un buen camino a seguir.
Espero eso ayude
El origen de su problema:
De forma predeterminada, hibernar carga perezosamente las colecciones (relaciones), lo que significa que cada vez que usas el collection
en tu código (aquí comments
el campo en Topic
clase), la hibernación lo obtiene de la base de datos, ahora el problema es que estás obteniendo la colección en tu controlador (donde está la sesión JPA). está cerrado). Esta es la línea de código que causa la excepción (donde está cargando la comments
colección):
Collection<Comment> commentList = topicById.getComments();
Obtiene una colección de "comentarios" (topic.getComments()) en su controlador (donde JPA session
terminó) y eso provoca la excepción. Además, si tuvieras la comments
colección en tu archivo jsp de esta manera (en lugar de tenerla en tu controlador):
<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>
Seguirías teniendo la misma excepción por el mismo motivo.
Resolviendo el problema:
Debido a que solo puede tener dos colecciones con FetchType.Eager
(colección recuperada con entusiasmo) 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 a FetchType
ansioso:
Si desea inicializar la colección de forma diferida 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 longitud 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 estará abierta un poco más y debido a eso puede cargar colecciones de manera diferida en sus archivos jsp y clases de controlador. .