但是,这有一个问题。 在我看来,异常是第二个最常见的异常(在NullPointerException之后),即LazyInitializationException。 问题在于会话通常为您的服务层打开,并且在您将实体返回到视图层后立即关闭。 当您尝试在视图中迭代未初始化的集合时(例如jsp),该集合将引发LazyInitializationException,因为它们所拥有的引用所在的会话已经关闭,并且无法获取这些项。
如何解决? 所谓的OpenSessionInView / OpenEntityManagerInView“模式”。 简而言之:您可以创建一个过滤器,以在请求启动时打开会话,并在呈现视图后(而不是在服务层完成后)关闭会话。 有人称其为反模式,因为它将持久性处理泄漏到视图层,并使设置复杂化。 我不会说那么糟糕:通常,它可以解决问题而不引入其他问题。 但是在我参与的所有最新项目中,我们没有使用OpenSessionInView,而且效果很好。
之所以能正常工作,是因为我们没有使用惰性集合。 但是,您会正确地指出,当您加载单个实体时,您将获取“整个世界”。 好吧,不。 * ToMany映射有两种类型:
- 值类型映射,集合在逻辑上不包含十几个元素。 在大多数情况下,这是@ElementCollection,还有@ * ToMany,它们带有诸如“ Category”或“ Price”之类的项,它们只是更复杂的值对象,但自身不包含任何其他映射。 这些类型的集合的另一个共同特征是它们通常与它们自己的实体一起显示在UI中。 例如,您最有可能要显示文章的类别。 对于这种类型的集合,EAGER是更好的选择。 无论如何,您都必须获取它们,为什么不让休眠(或任何jpa实现)想到一些巧妙的连接呢? 就像我说的那样-逻辑上集合不超过一打或十二个,因此获取它们不会对性能造成影响。 而且,从逻辑上讲,它们不会与它们一起获取大对象图。
- 大型核心实体之间的映射。 可以是“用户所下的所有订单”或“组织中的所有用户”,“供应商的所有项目”等。您当然不想急于获取它们。 因为如果您为一个组织获取2000个用户,那么每个组织又有1000个订单,而一个订单平均有3个项目,这反过来又包含所有购买该项目的人的集合。.您将最终拥有整个数据库在记忆中。 显然您需要惰性集合,对吗? 好吧,不。 在这种情况下,您根本不应该使用集合映射。 在99%的情况下,这些类型的关系显示在UI的页面列表中。 或在搜索结果中。 它们永远不会(也永远不会)全部显示在一个屏幕上(或者,如果您的应用程序提供了类似REST API之类的东西,则很少应该在一个API调用中返回它们)。 您必须对其进行查询,并使用query.setMaxResults和query.setFirstResult()(或使用一些限制性条件来限制它们)。 此外,对集合进行映射意味着有人会在某个时候尝试使用它们,这可能会失败。 并且如果对象已序列化(xml,json等),则将获取集合内容。 您几乎肯定不想发生的事情。 (这里的想法草案:JPA可以有一个PagedList集合,该集合将允许分页的延迟提取,从而消除了查询的需要)
所以我刚才说的是-永远不要使用惰性集合。 将eager集合用于非常简单的浅表映射,将分页查询用于较大的映射。
好吧,不完全是。 延迟集合在那里并且它们有应用,尽管它是相当有限的。 或者,至少它们比所使用的方法不太适用。 这是我发现适用的示例场景。 在我的附带项目中,我有一个Message实体,并且它包含一个Picture实体的集合。 用户上载图片时,它将存储在该集合中。 一条消息最多可以包含10张图片,因此非常希望收藏。 但是,然后,Message是最常用的实体–实际上是在每个请求中获取的。 但是只有一些消息带有图片(您的信息流中有多少条推文有图片上传?)。 因此,我不想让休眠状态进行查询只是为了查找给定消息没有图片。 因此,我将图片数量存储在一个单独的字段中,使图片集合变得懒惰,并且仅在图片数量> 0时才手动对其进行Hibernate.initialize(..)。
因此,在某些情况下,当实体具有属于上述第一类的可选集合时(“小型浅表集合”)。 因此,如果它很小,很浅并且是可选的(例如,在不到20%的情况下使用),则应该使用Lazy来保存不必要的查询。
其他方面–懒惰的收藏会让您的生活更艰难。
参考:在Bozho的技术博客上, 避免与我们的JCG合作伙伴 Bozho一起使用懒惰的JPA Collections 。
相关文章 :
翻译自: https://www.javacodegeeks.com/2011/10/avoid-lazy-jpa-collections.html