用hibernate.enable_lazy_load_no_trans解决Hibernate的Lazy-Init问题
我一直在遭受臭名昭着的hibernateexception
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
现在社区正在欢呼
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
说它解决了这个问题,但使用它注意 。
他们是什么意思,谨慎使用它? 这个属性实际上做了什么?
请给我任何见解。 提前致谢。
这种方法的问题是你可以有N + 1的效果。
想象一下,你有以下的实体:
public class Person{ @OneToMany // default to lazy private List<Order> orderList; }
如果你有一个返回10K的人的报告,并且在这个报告中你执行代码person.getOrderList()
那么JPA / Hibernate将执行10K的查询。 这是N + 1的效果,你将不能控制所有将被执行的查询。
现在想象一下Order就像下面这样:
public class Order{ @OneToMany // default to lazy private List<EmailSent> emailSentList; }
现在想象一下,你有一个迭代person.getOrderList()
和每个Order order
你会做一个order.getEmailSentList()
。 你现在能看到这个问题吗?
对于LazyInitializationException,你可以有一些解决scheme:
- 使用OpenInSessionInView方法。 您将需要创build一个将打开和closures事务的WebFilter。 问题在于N + 1效应。
- 使用hibernate.enable_lazy_load_no_transconfiguration,这是一个hibernate,如果需要,您将无法将您的项目移植到其他JPA提供程序。 你也可以有N + 1的效果。
- 使用名为PersistenceContext Extended的EJB特性。 有了这个,你将保持打开几个交易的上下文。 问题是:N + 1效应可能发生,使用大量的服务器内存(实体将保持pipe理)
- 在查询中使用FETCH。 有了这种方法,你可以做一个JPQL / HQL如下:
select p from Person p join fetch p.orderList
。 使用这个查询,你将从数据库加载你的列表,并且不会有N + 1的效果。 问题是你将需要为每个案例写一个JPQL。
如果您还有任何问题,请查看以下链接: http : //uaihebert.com/four-solutions-to-the-lazyinitializationexception
这与我们如何利用Session的概念利用Hibernate对可重复读取语义的强制性相反。 当一个对象第一次加载时,如果在会话生命周期内再次引用该对象,则返回该对象在DB中是否发生了更改的同一对象的IRRESPECTIVE。 这是由hibernate自动提供的可重复读取语义。
使用此设置,您没有提供此保证的会话,因此如果您现在访问此数据,您将获得最新版本的数据。
这可能是好的。 但是考虑一下这个对象在某个地方长时间保存的情况,并且数据发生了很大的变化,所以延迟读取的数据与在会话存活时已经载入的数据大不相同。 这是你需要关心的。
简单的说,如果你的程序不受以下情况的影响,你可以安全地使用这个设置:在会话中已经获取的数据是如何过时的,
但是,如果这个问题( 你的程序暴露于时间问题,什么时候它可能正常工作,又失败了 )是一个问题,然后在会话中获取所有必要的数据。
解决LazyInitializationException的最好方法是在实体查询中使用JOIN FETCH指令。
EAGER加载对性能不利。 此外,还有反模式,如:
- 在视图中打开会话
-
hibernate.enable_lazy_load_no_trans
你不应该使用它,因为它们需要数据库连接为用户界面渲染打开(Open View in View),或者每个在初始持久化上下文( hibernate.enable_lazy_load_no_trans
)之外获取的惰性关联都需要数据库连接。 。
有时候,你甚至不需要实体,而DTO投影就更好了 。
可能是因为有更好的解决scheme,比如@Transactional ,其中开放和closures会话遵循一个非常常见的“打开会话模式”,然后将所有内容封装在try-catch-finally; catch回滚并最终closures会话。 这个注释通常在Web应用程序和服务的请求级别。
或者,如果您需要更精细的控制,则可以使用SessionFactory手动打开会话。
正如其他人所说,懒加载是你需要注意的事情。 这不是一个银弹,但它可以是非常有帮助的。 一般来说,如果你的应用程序devise有很多小的请求,那么它的确定。
急切的加载也可能非常糟糕。 例如,当你的对象模型有许多多对多的关系,但是你的请求不会使用多于一层的数据。
或者你现在可以忘记整个事情。 使用延迟加载,直到它成为一个问题。 如果是这样的话,无论如何你还是会更喜欢Mybatis。