解决“未能懒散地初始化一个angular色集合”的例外

我有这个问题:

org.hibernate.LazyInitializationException:未能延迟初始化angular色集合:mvc3.model.Topic.comments,没有会话或会话已closures

这是模型:

@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; } } 

调用模型的控制器如下所示:

 @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时exception升级。 在c:forEach循环中

如果您知道每次检索Topic时都希望看到所有Comment请将您的字段映射更改为:

 @OneToMany(fetch = FetchType.EAGER, mappedBy = "topic", cascade = CascadeType.ALL) private Collection<Comment> comments = new LinkedHashSet<Comment>(); 

默认情况下,集合是延迟加载的,如果您想了解更多信息,请参阅本文。

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

(1)使用Hibernate.initialize

 Hibernate.initialize(topics.getComments()); 

(2)使用JOIN FETCH

您可以在JPQL中使用JOIN FETCH语法来显式获取子集合。 这是一些像EAGER抓取的东西。

(3)使用OpenSessionInViewFilter

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

你问题的由来:

默认情况下,hibernate懒惰地加载集合(关系),这意味着当你在你的代码中使用collection (这里是Topic类中的comments字段)时,hibernate从数据库中获取它,现在问题是你正在控制器中获取集合JPA会话是closures的)。这是导致exception的代码行(您要加载comments集合的地方):

  Collection<Comment> commentList = topicById.getComments(); 

您在控制器( JPA session已经结束)中收到“comments”集合(topic.getComments())并导致exception。 另外,如果你已经像这样在你的jsp文件中获得了comments集合(而不是在你的控制器中获取):

 <c:forEach items="topic.comments" var="item"> //some code </c:forEach> 

出于同样的原因,您仍然会有相同的exception。

解决问题:

因为在一个Entity类中你只能拥有两个带有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 session的长度,或者像文档中所说的那样,它被用来"to allow for lazy loading in web views despite the original transactions already being completed." 所以JPA会话将以这种方式打开一段时间,因此可以在jsp文件和控制器类中延迟加载集合。

我知道这是一个老问题,但我想帮忙。 你可以把事务注释放在你需要的服务方法上,在这种情况下findTopicByID(id)应该有

 @Transactional(propagation=Propagation.REQUIRED, readOnly=true, noRollbackFor=Exception.class) 

关于这个注释的更多信息可以在这里find

关于其他解决scheme:

 fetch = FetchType.EAGER 

这不是一个好的做法,只有在必要时才应该使用。

 Hibernate.initialize(topics.getComments()); 

hibernate初始化器将你的类绑定到hibernate技术上。 如果你的目标是灵活的,不是一个好的方法。

希望它有帮助

原因是当你使用延迟加载时,会话被closures。

有两个解决scheme。

  1. 不要使用延迟加载。

    在XML中设置lazy=false或设置@OneToMany(fetch = FetchType.EAGER)在注释中。

  2. 使用延迟加载。

    在XML中设置lazy=true或设置@OneToMany(fetch = FetchType.LAZY)在注释中。

    并在您的web.xml添加OpenSessionInViewFilter filter

细节看我的POST 。

为了延迟加载集合,必须有一个活动的会话。 在一个networking应用程序有两种方法来做到这一点。 您可以使用“ 打开会话视图”模式,在该模式下,您可以使用拦截器在请求开始时打开会话,并在最后closures该会话。 这样做的风险是你必须有可靠的exception处理,或者你可以绑定所有的会话,你的应用程序可能会挂起。

处理这个问题的另一种方法是收集您的控制器中需要的所有数据,closures会话,然后将数据填充到您的模型中。 我个人更喜欢这种方法,因为它似乎更接近MVC模式的精神。 此外,如果您以这种方式从数据库中获取错误,则可以比处理视图渲染器中的错误更好地处理它。 在这种情况下你的朋友是Hibernate.initialize (myTopic.getComments())。 您还必须将该对象重新连接到会话,因为您正在使用每个请求创build一个新的事务。 使用session.lock(myTopic,LockMode.NONE)。

如果你正试图在一个实体和一个Collection对象或者一个Java对象列表(例如Longtypes)之间build立关系,它会像这样:

 @ElementCollection(fetch = FetchType.EAGER) public List<Long> ids; 

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

 @PersistenceContext(type = PersistenceContextType.EXTENDED) 

这个问题是由closureshibernate会话访问属性造成的。 控制器中没有hibernate事务。

可能的解决scheme:

  1. 在服务层 (使用@Transactional) 执行所有这些逻辑,而不是在控制器中。 应该有合适的位置来做到这一点,它是应用程序的逻辑的一部分,而不是在控制器(在这种情况下,加载模型的接口)。 服务层中的所有操作都应该是事务性的。 即:将此行移动到TopicService.findTopicByID方法:

    集合commentList = topicById.getComments();

  2. 使用“渴望”而不是“懒惰” 。 现在你不使用'懒惰'..这不是一个真正的解决scheme,如果你想使用懒惰,像临时(非常临时)的解决方法。

  3. 在Controller中使用@Transactional 。 在这里不应该使用,你是混合服务层和演示文稿,这不是一个好的devise。
  4. 使用OpenSessionInViewFilter ,报道很多缺点,可能不稳定。

一般来说,最好的解决scheme是1。

这是我最近面临的问题,我解决了使用

 <f:attribute name="collectionType" value="java.util.ArrayList" /> 

在这里更详细的解释,这节省了我的一天。

为了解决这个问题,我只是错过了这一行

 <tx:annotation-driven transaction-manager="myTxManager" /> 

在应用程序上下文文件中。

没有考虑到方法上的@Transactional注解。

希望答案会帮助别人

控制器上的@Transactional注释缺失

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

对于那些与Criteria合作的人来说 ,我发现这一点

 criteria.setFetchMode("lazily_fetched_member", FetchMode.EAGER); 

做了我所需要的一切。

集合的初始获取模式设置为FetchMode.LAZY来提供性能,但是当我需要数据时,我只需添加该行并享受完全填充的对象。

你的列表是懒加载,所以列表没有加载。 打电话到名单是不够的。 在Hibernate.initialize中使用,以便初始化列表。 如果dosnt在列表元素上运行并为每个元素调用Hibernate.initialize。 这需要从事务范围返回之前。 看这个post。
search –

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

在我的情况下,以下代码是一个问题:

 entityManager.detach(topicById); topicById.getComments() // exception thrown 

因为它从数据库中分离出来,Hibernate在需要时不再从字段中检索列表。 所以我在分离前初始化它:

 Hibernate.initialize(topicById.getComments()); entityManager.detach(topicById); topicById.getComments() // works like a charm 
 @Controller @RequestMapping(value = "/topic") @Transactional 

我通过添加@Transaction解决这个问题,我想这可以使会话打开

把它添加到你的persistence.xml

 <property name="hibernate.enable_lazy_load_no_trans" value="true" /> 

我解决了使用List而不是Set:

 private List<Categories> children = new ArrayList<Categories>();