【问题标题】:Theoretical question on Hibernate Lazy LoadingHibernate延迟加载的理论问题
【发布时间】:2020-07-01 14:04:17
【问题描述】:

我了解到 Hibernate 仅在需要时才允许通过使用延迟加载来查询子元素。

如果我们有:

@Entity
public class Payment {

 ...

 @ManyToOne(fetch = FetchType.LAZY)
 private Debtor debtor;

}

我希望当我从数据库中获取付款时,Hibernate 在债务人属性中设置一个占位符并仅在严格要求时获取债务人

因此,如果我使用 getter 方法从我的付款对象中获取债务人:

Debtor debtor = payment.getDebtor();

我预计线程会阻塞,直到 Hibernate 执行 SELECT 查询并返回 Debtor 对象。

那么为什么我总是得到一个 HibernateLazyLoading 异常,它迫使我在 PaymentRepository 中编写一个自定义提取查询,从而减慢了我的初始查询速度因为我会使用 EAGER FetchType

那么,如果 FetchType.LAZY 不能按自然预期的方式工作,为什么还会存在呢?

【问题讨论】:

  • 您能否展示出现此问题的示例代码。根据我的经验,延迟加载异常仅在您尝试访问事务外部实体的延迟加载字段时发生。当实体不再附加时。

标签: java spring hibernate spring-data hibernate-mapping


【解决方案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();

问题

那么为什么我总是得到一个 HibernateLazyLoading 异常 迫使我在 PaymentRepository 中编写自定义提取查询, 减慢我的初始查询,因为我会使用 EAGER FetchType?

因为Hibernate.closePersistentContext()之前发生过。

如果 FetchType.LAZY 不能正常工作,为什么还要存在 预期?

因为我们并不总是需要完整的实体图网络。 我们可以使用 JPQL (HQL)、标准和预测来加载实体的一部分。我们需要向 Hibernate 解释实体是如何关联的,所以我们需要添加关联,例如 @ManyToOne

这里有个小问题:映射有两个目的

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

因此,从对象映射断开加载的最简单方法是FetchType.LAZY

简单的规则

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

【讨论】:

    【解决方案2】:

    那么为什么我总是得到一个 HibernateLazyLoading 异常,它迫使我在 PaymentRepository 中编写自定义提取查询,从而减慢我的初始查询,因为我会使用 EAGER FetchType?

    因为这句话并不完全正确:

    我希望当我从数据库 Hibernate 获取付款时,在债务人属性中设置一个占位符,并仅在严格要求时获取债务人。

    Debtor 确实会被获取,如果它被访问,但前提是它可以被获取。如果您在事务中运行它(例如通过使用@Transactional 注释方法),则不会发生错误,因为与数据库的连接没有返回到池/关闭。

    在您的情况下,数据被提取,Debtor 被包裹在代理中并且连接丢失。如果你尝试访问它,LazyInitializationException 会被抛出。

    P.S.:不建议使用事务来避免LazyInitializationException,因为可能会出现性能问题。如果您获取父级,然后迭代多个(例如 N 个)延迟获取的子级,则会触发对数据库的 N + 1 个查询。

    【讨论】:

      最近更新 更多