我有这样一个问题:
org.hibernate.LazyInitializationException:惰性初始化role: mvc3.model.Topic.comments集合失败,没有会话或会话已关闭
下面是模型:
@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;
}
}
调用model的控制器如下所示:
@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);
}
}
jsp页面看起来如下所示:
<%@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>
在查看jsp时,将引发异常。在c:forEach循环的行中
这是一个老问题,但下面的信息可以帮助人们寻找答案。
@VladMihalcea的回答很有用。你不能依赖FetchType。相反,您应该在需要时将注释加载到Topic实体中。
如果您没有显式地定义您的查询以便您可以指定连接获取,那么使用@NamedEntityGraph和@EntityGraph您可以覆盖FetchType。LAZY(默认情况下@OneToMany关联使用LAZY)在运行时加载注释,只在需要时加载Topic。这意味着您将注释的加载限制在那些真正需要注释的方法(查询)上。JPA定义的实体图:
实体图可以与find方法一起使用,也可以作为查询提示
覆盖或增强FetchType语义。
您可以基于这里的JPA示例使用它。或者,如果您使用Spring Data JPA,那么您可以基于Spring提供的示例来使用它。
为了延迟加载集合,必须有一个活动会话。在web应用中,有两种方法可以做到这一点。您可以使用Open Session In View模式,其中使用拦截器在请求开始时打开会话,并在结束时关闭会话。风险在于你必须有可靠的异常处理,否则你可能会绑定所有的会话,你的应用程序可能会挂起。
The other way to handle this is to collect all the data you need in your controller, close your session, and then stuff the data into your model. I personally prefer this approach, as it seems a little closer to the spirit of the MVC pattern. Also if you get an error from the database this way you can handle it a lot better than if it happens in your view renderer. Your friend in this scenario is Hibernate.initialize(myTopic.getComments()). You will also have to reattach the object to the session, since you're creating a new transaction with every request. Use session.lock(myTopic,LockMode.NONE) for that.
问题的根源:
默认情况下,hibernate惰性加载集合(关系),这意味着无论何时在代码中使用集合(这里是comments字段)
主题课)
hibernate从数据库中获取,现在的问题是,您正在获得控制器中的集合(其中
JPA会话已关闭)。这是导致异常的代码行
(你正在加载注释集合):
Collection<Comment> commentList = topicById.getComments();
您在您的控制器(JPA会话已经结束的地方)中获得“comments”集合(topic.getComments()),这会导致异常。如果你有
JSP文件中的注释集合像这样(而不是在控制器中获取它):
<c:forEach items="topic.comments" var="item">
//some code
</c:forEach>
出于同样的原因,仍然会出现相同的异常。
解决问题:
因为FetchType只能有两个集合。实体类中的Eager(急切获取的集合),因为延迟加载更多
比急切加载更有效,我认为这种解决问题的方法比仅仅将FetchType更改为急切更好:
如果你想初始化懒集合,并让这个工作,
最好将这段代码添加到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>
这段代码所做的是它将增加您的JPA会话的长度,或者正如文档所说,它被用于“允许在web视图中延迟加载,尽管原始事务已经完成。
这样,JPA会话打开的时间会更长一点
您可以在JSP文件和控制器类中惰性加载集合。
模型类Topic中的集合注释是惰性加载的,如果你不使用fetch = FetchType注释它,这是默认的行为。热切的特别。
findTopicByID服务很可能正在使用无状态Hibernate会话。无状态会话没有第一级缓存,即没有持久性上下文。稍后,当您尝试迭代注释时,Hibernate将抛出异常。
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mvc3.model.Topic.comments, no session or session was closed
解决方案可以是:
使用fetch = FetchType注释注释。急切的
@OneToMany(fetch = FetchType。mappedBy = "topic", cascade = CascadeType.ALL)
private Collection<Comment> comments = new LinkedHashSet<Comment>();
如果您仍然希望延迟加载注释,请使用Hibernate的有状态会话,这样您就可以在需要时获取注释。