我有这样一个问题:
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循环的行中
问题的根源:
默认情况下,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文件和控制器类中惰性加载集合。
此问题是由于在关闭hibernate会话的情况下访问属性造成的。控制器中没有hibernate事务。
可能的解决方式:
Do all this logic, in the service layer, (with the @Transactional), not in the controller. There should be the right place to do this, it is part of the logic of the app, not in the controller (in this case, an interface to load the model). All the operations in the service layer should be transactional.
i.e.: Move this line to the TopicService.findTopicByID method:
Collection commentList = topicById.getComments();
Use 'eager' instead of 'lazy'. Now you are not using 'lazy' .. it is not a real solution, if you want to use lazy, works like a temporary (very temporary) workaround.
use @Transactional in the Controller. It should not be used here, you are mixing service layer with presentation, it is not a good design.
use OpenSessionInViewFilter, many disadvantages reported, possible instability.
一般来说,最佳解是1。
在第二次执行生成JWT令牌的方法后,我得到了这个错误。
line user.getUsersRole().stream().forEachOrdered((ur) -> roles.add(ur. getroleid ()));生成错误。
// MyUserDetails.java
@Service
public class MyUserDetails implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String email) {
/* ERROR
/* org.hibernate.LazyInitializationException: failed to
/* lazily initialize a collection of role:
/* com.organizator.backend.model.User.usersRole,
/* could not initialize proxy - no Session */
user.getUsersRole().stream().forEachOrdered((ur) ->
roles.add(ur.getRoleId()));
在我的例子中,@Transactional注释解决了它,
// MyUserDetails.java
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyUserDetails implements UserDetailsService {
@Override
@Transactional // <-- added
public UserDetails loadUserByUsername(String email) {
/* No Error */
user.getUsersRole().stream().forEachOrdered((ur) ->
roles.add(ur.getRoleId()));