我有这样一个问题:

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循环的行中


当前回答

不是最好的解决方案,但对于那些面临LazyInitializationException的人,特别是在序列化上,这将有所帮助。在这里,您将检查惰性初始化属性并将其设置为null。为此,创建以下类

public class RepositoryUtil {
    public static final boolean isCollectionInitialized(Collection<?> collection) {
        if (collection instanceof PersistentCollection)
            return ((PersistentCollection) collection).wasInitialized();
        else 
            return true;
    }   
}

在你的实体类中,你有一个惰性初始化的属性,添加如下所示的方法。在这个方法中添加所有延迟加载属性。

public void checkLazyIntialzation() {
    if (!RepositoryUtil.isCollectionInitialized(yourlazyproperty)) {
        yourlazyproperty= null;
    }

在加载数据的所有位置之后调用这个checklazyinitialize()方法。

 YourEntity obj= entityManager.find(YourEntity.class,1L);
  obj.checkLazyIntialzation();

其他回答

你的列表是惰性加载的,所以列表没有加载。 仅仅通过电话进入名单是不够的。 在Hibernate中使用。初始化以初始化列表。 如果不起作用,运行列表元素并调用Hibernate。初始化每一个。 这需要在从事务范围返回之前进行。 看看这篇文章。 搜寻-

Node n = // .. get the node
Hibernate.initialize(n); // initializes 'parent' similar to getParent.
Hibernate.initialize(n.getChildren()); // pass the lazy collection into the session 

此问题是由于在关闭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。

我发现将@PersistenceContext声明为EXTENDED也解决了这个问题:

@PersistenceContext(type = PersistenceContextType.EXTENDED)

导致这个问题的原因是,当到数据库的“连接”关闭时,代码正在访问一个惰性JPA关系(持久上下文是Hibernate/JPA的正确名称)。

在Spring Boot中解决这个问题的一个简单方法是定义一个服务层并使用@Transactional注释。方法中的注释创建一个事务,该事务将传播到存储库层,并在方法完成之前保持打开持久性上下文。如果您访问事务方法中的集合,Hibernate/JPA将从数据库中获取数据。

在您的情况下,您只需要在TopicService中使用@Transactional注释findTopicByID(id)方法,并强制在该方法中获取集合(例如,通过询问其大小):

    @Transactional(readOnly = true)
    public Topic findTopicById(Long id) {
        Topic topic = TopicRepository.findById(id).orElse(null);
        topic.getComments().size();
        return topic;
    }

根据我的经验,我有以下方法来解决著名的LazyInitializationException:

(1)使用Hibernate.initialize

Hibernate.initialize(topics.getComments());

(2)使用JOIN FETCH

您可以在JPQL中使用JOIN FETCH语法显式地取出子集合。这有点像EAGER取回。

3)使用OpenSessionInViewFilter

LazyInitializationException经常发生在视图层。如果你使用Spring框架,你可以使用OpenSessionInViewFilter。但是,我不建议你这样做。如果使用不当,可能会导致性能问题。