我有这样一个问题:

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会话的情况下访问属性造成的。控制器中没有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。

其他回答

@Controller
@RequestMapping(value = "/topic")
@Transactional

我通过添加@Transactional来解决这个问题,我认为这可以使会话打开

这是一个老问题,但下面的信息可以帮助人们寻找答案。

@VladMihalcea的回答很有用。你不能依赖FetchType。相反,您应该在需要时将注释加载到Topic实体中。

如果您没有显式地定义您的查询以便您可以指定连接获取,那么使用@NamedEntityGraph和@EntityGraph您可以覆盖FetchType。LAZY(默认情况下@OneToMany关联使用LAZY)在运行时加载注释,只在需要时加载Topic。这意味着您将注释的加载限制在那些真正需要注释的方法(查询)上。JPA定义的实体图:

实体图可以与find方法一起使用,也可以作为查询提示 覆盖或增强FetchType语义。

您可以基于这里的JPA示例使用它。或者,如果您使用Spring Data JPA,那么您可以基于Spring提供的示例来使用它。

通过使用hibernate @Transactional注释,如果你从数据库中获得一个具有惰性获取属性的对象,你可以像这样简单地获取这些属性:

@Transactional
public void checkTicketSalePresence(UUID ticketUuid, UUID saleUuid) {
        Optional<Ticket> savedTicketOpt = ticketRepository.findById(ticketUuid);
        savedTicketOpt.ifPresent(ticket -> {
            Optional<Sale> saleOpt = ticket.getSales().stream().filter(sale -> sale.getUuid() == saleUuid).findFirst();
            assertThat(saleOpt).isPresent();
        });
}

这里,在Hibernate代理管理的事务中,调用ticket.getSales()执行另一个查询来获取sales,因为您显式地请求了它。

不是最好的解决方案,但对于那些面临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();

控制器上缺少@Transactional注释

@Controller
@RequestMapping("/")
@Transactional
public class UserController {
}