我有这样一个问题:
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提供的示例来使用它。
在第二次执行生成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()));
问题的根源:
默认情况下,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文件和控制器类中惰性加载集合。