解决“未能懒散地初始化一个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。
-
不要使用延迟加载。
在XML中设置
lazy=false
或设置@OneToMany(fetch = FetchType.EAGER)
在注释中。 -
使用延迟加载。
在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:
-
在服务层 (使用@Transactional) 执行所有这些逻辑,而不是在控制器中。 应该有合适的位置来做到这一点,它是应用程序的逻辑的一部分,而不是在控制器(在这种情况下,加载模型的接口)。 服务层中的所有操作都应该是事务性的。 即:将此行移动到TopicService.findTopicByID方法:
集合commentList = topicById.getComments();
-
使用“渴望”而不是“懒惰” 。 现在你不使用'懒惰'..这不是一个真正的解决scheme,如果你想使用懒惰,像临时(非常临时)的解决方法。
- 在Controller中使用@Transactional 。 在这里不应该使用,你是混合服务层和演示文稿,这不是一个好的devise。
- 使用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>();