关于 Hibernate 延迟加载的理论问题
Theoretical question on Hibernate Lazy Loading
我了解到 Hibernate 允许仅在必要时通过延迟加载查询子元素。
所以如果我们有:
@Entity
public class Payment {
...
@ManyToOne(fetch = FetchType.LAZY)
private Debtor debtor;
}
我希望当我从 Hibernate 数据库中获取付款时,在债务人属性中设置一个占位符并且仅在严格要求时才获取债务人。
因此,如果我使用 getter 方法从我的付款对象中获取债务人:
Debtor debtor = payment.getDebtor();
我希望线程阻塞,直到 Hibernate 执行 SELECT 查询和 return Debtor 对象。
那么为什么我总是得到一个 HibernateLazyLoading 异常 这迫使我在 PaymentRepository 中编写一个 自定义获取查询 ,减慢速度我的初始查询 因为我会使用 EAGER FetchType?
那么,如果这个 FetchType.LAZY
不能正常工作,为什么会存在呢?
So why the heck I always get an HibernateLazyLoading exception which obliges me to write a custom fetch query in the PaymentRepository, slowing down my initial query AS I would have used an EAGER FetchType?
因为这句话不完全正确:
I expect that when I fetch a Payment from the Database Hibernate set a placeholder in the debtor attribute and fetches the Debtor only when it's stricly required.
Debtor
确实会被获取,如果它被访问,但前提是它可以被获取。如果你在事务中 运行 它(例如通过注释方法 @Transactional
),则不会发生错误,因为与数据库的连接不会返回到池/关闭。
在您的情况下,数据已获取,Debtor
被包裹在代理中并且连接丢失。如果您尝试访问它,则会抛出 LazyInitializationException
。
P.S.: 不建议使用事务来避免LazyInitializationException
,因为可能会出现性能问题。如果您获取父项,然后迭代多个(比如 N 个)延迟获取的子项,则会执行 N + 1 次对数据库的查询。
我想指定@Andronicus 的答案,因为答案不准确。
LazyInitializationException
它与@Transactional
、事务或打开/关闭的连接没有严格关系。
行为很简单(下面有伪代码)
没有LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Debtor debtor = payment.getDebtor();
Hibernate.closePersistentContext();
和LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Hibernate.closePersistentContext();
Debtor debtor = payment.getDebtor();
问题
So why the heck I always get an HibernateLazyLoading exception which
obliges me to write a custom fetch query in the PaymentRepository,
slowing down my initial query AS I would have used an EAGER FetchType?
因为Hibernate.closePersistentContext()
以前某处发生过。
So why does this FetchType.LAZY exists if it doesn't work as naturally
expected?
因为我们并不总是需要实体图的完整网络。
我们可以使用 JPQL (HQL)、标准和投影来加载实体的各个部分。我们需要向 Hibernate 解释实体之间的关系,因此我们需要添加关联,例如 @ManyToOne
.
这里有个小问题:映射有两个作用
- 向 Hibernate 解释实体之间的关系
- 保存/加载实体
所以从对象映射中断开加载的最简单方法是FetchType.LAZY
。
简单规则
始终在任何地方使用FetchType.LAZY
,并在需要的地方获取实体图的必要部分。
我了解到 Hibernate 允许仅在必要时通过延迟加载查询子元素。
所以如果我们有:
@Entity
public class Payment {
...
@ManyToOne(fetch = FetchType.LAZY)
private Debtor debtor;
}
我希望当我从 Hibernate 数据库中获取付款时,在债务人属性中设置一个占位符并且仅在严格要求时才获取债务人。
因此,如果我使用 getter 方法从我的付款对象中获取债务人:
Debtor debtor = payment.getDebtor();
我希望线程阻塞,直到 Hibernate 执行 SELECT 查询和 return Debtor 对象。
那么为什么我总是得到一个 HibernateLazyLoading 异常 这迫使我在 PaymentRepository 中编写一个 自定义获取查询 ,减慢速度我的初始查询 因为我会使用 EAGER FetchType?
那么,如果这个 FetchType.LAZY
不能正常工作,为什么会存在呢?
So why the heck I always get an HibernateLazyLoading exception which obliges me to write a custom fetch query in the PaymentRepository, slowing down my initial query AS I would have used an EAGER FetchType?
因为这句话不完全正确:
I expect that when I fetch a Payment from the Database Hibernate set a placeholder in the debtor attribute and fetches the Debtor only when it's stricly required.
Debtor
确实会被获取,如果它被访问,但前提是它可以被获取。如果你在事务中 运行 它(例如通过注释方法 @Transactional
),则不会发生错误,因为与数据库的连接不会返回到池/关闭。
在您的情况下,数据已获取,Debtor
被包裹在代理中并且连接丢失。如果您尝试访问它,则会抛出 LazyInitializationException
。
P.S.: 不建议使用事务来避免LazyInitializationException
,因为可能会出现性能问题。如果您获取父项,然后迭代多个(比如 N 个)延迟获取的子项,则会执行 N + 1 次对数据库的查询。
我想指定@Andronicus 的答案,因为答案不准确。
LazyInitializationException
它与@Transactional
、事务或打开/关闭的连接没有严格关系。
行为很简单(下面有伪代码)
没有LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Debtor debtor = payment.getDebtor();
Hibernate.closePersistentContext();
和LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Hibernate.closePersistentContext();
Debtor debtor = payment.getDebtor();
问题
So why the heck I always get an HibernateLazyLoading exception which obliges me to write a custom fetch query in the PaymentRepository, slowing down my initial query AS I would have used an EAGER FetchType?
因为Hibernate.closePersistentContext()
以前某处发生过。
So why does this FetchType.LAZY exists if it doesn't work as naturally expected?
因为我们并不总是需要实体图的完整网络。
我们可以使用 JPQL (HQL)、标准和投影来加载实体的各个部分。我们需要向 Hibernate 解释实体之间的关系,因此我们需要添加关联,例如 @ManyToOne
.
这里有个小问题:映射有两个作用
- 向 Hibernate 解释实体之间的关系
- 保存/加载实体
所以从对象映射中断开加载的最简单方法是FetchType.LAZY
。
简单规则
始终在任何地方使用FetchType.LAZY
,并在需要的地方获取实体图的必要部分。