我一直患有臭名昭着的hibernate异常
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
现在社区正在欢呼
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
说它解决了问题,但小心使用。
谨慎使用它们意味着什么?这个属性实际上做了什么?
请给我任何见解。提前谢谢。
答案 0 :(得分:40)
这种方法的问题在于你可以获得N + 1效果。
想象一下,你有以下实体:
public class Person{
@OneToMany // default to lazy
private List<Order> orderList;
}
如果您有一个返回10K人的报告,并且如果在此报告中您执行代码person.getOrderList()
,则JPA / Hibernate将执行10K的查询。这是N + 1效果,您将无法控制将要执行的所有查询。
现在想象订单如下:
public class Order{
@OneToMany // default to lazy
private List<EmailSent> emailSentList;
}
现在想象一下,您对person.getOrderList()
进行了迭代,对于每个Order order
,您将进行order.getEmailSentList()
。你现在能看到问题吗?
对于LazyInitializationException,您可以使用一些解决方案:
select p from Person p join fetch p.orderList
。使用此查询,您将从数据库加载列表,并且不会产生N + 1效果。问题是你需要为每个案例编写一个JPQL。如果您仍有任何问题,请查看以下链接:
答案 1 :(得分:10)
这违背了我们如何利用Hibernate利用Session概念实现可重复读取语义的方法。 首次加载对象时,如果在会话的生命周期内再次引用该对象,则返回相同的对象IRRESPECTIVE,该对象是否在DB中发生了更改。这是hibernate自动提供的可重复读取语义。
使用此设置,您没有提供此保证的会话,因此,如果您现在访问此数据,您将获得最新版本的数据。
这可能没问题。但是考虑这个对象在某个地方长时间保存并且数据发生了很大变化的情况,因此延迟获取的数据与会话处于活动状态时已经加载的数据有很大不同。这是你需要关注的。
简单来说,如果您的程序是,您可以安全地使用此设置 不受影响:进入时已经获取的数据的陈旧程度 会话将暂时从会话中获取的数据
但是如果这个(你的程序暴露于计时问题,它可能正常工作一次并且再次失败)是一个问题,那么在会话期间获取所有必要的数据。
答案 2 :(得分:4)
The best way to solve the LazyInitializationException是在实体查询中使用JOIN FETCH指令。
EAGER loading对性能不利。此外,还有反模式,如:
您永远不应该使用它,因为它们要求为UI呈现打开数据库连接(在视图中打开会话),或者在初始持久化上下文之外获取的每个惰性关联都需要数据库连接({ {1}})。
有时,您甚至不需要实体,a DTO projection is even better。
答案 3 :(得分:2)
可能是因为有更好的解决方案,比如@Transactional,其中打开和关闭会话遵循一个非常常见的模式“打开会话然后将所有内容包装在try-catch-finally中; catch回滚并最终关闭会话“。此注释通常位于Web应用程序和服务的请求级别。
或者,如果您需要更精细的控制,可以使用SessionFactory手动打开会话。
正如其他人所说,延迟加载是你需要注意的事情。它不是一颗银弹,但它可能非常有用。通常,如果您的应用程序设计为包含许多小请求,那么就可以了。
急切加载也可能非常糟糕。例如,当您的对象模型具有许多多对多关系但您的请求不使用多于一个级别的数据时。
或者你现在可以忘掉整件事。使用延迟加载直到它成为一个问题。如果确实如此,无论如何你都会更好地使用Mybatis。