关于 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.

这里有个小问题:映射有两个作用

  1. 向 Hibernate 解释实体之间的关系
  2. 保存/加载实体

所以从对象映射中断开加载的最简单方法是FetchType.LAZY

简单规则

始终在任何地方使用FetchType.LAZY,并在需要的地方获取实体图的必要部分。