使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?

Posted

技术标签:

【中文标题】使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?【英文标题】:Prevent lazy loading in JPA/Hibernate with FetchType.LAZY (especially when using @Transactional)? 【发布时间】:2022-01-16 14:40:27 【问题描述】:

我四处搜寻,但只得到与我的问题相反的人。假设我们有:

@Entity
class Book 
  ...
  @ManyToOne(fetch = FetchType.LAZY)
  private Author author;

JPA/Hibernate 中是否有(最好是全局的)属性/方式来防止懒惰地加载作者(或任何实体)?

别误会,我不想使用 EAGER fetch。我想防止初级人员意外调用 book.getAuthor().getName() 并进行另一个数据库调用。很多人都在寻求修复他们的 LazyInitializationException,但我基本上想强制抛出这样的异常,即使存在活动会话(使用 @Transactional 时很容易犯错误)。但是,如果您在 JPQL 查询中正确使用“JOIN FETCH Author”,我仍然希望获取作者。

我的特殊用例是使用 Spring 和 GraphQL。 @Transactional 在会话打开时很容易隐藏并避免 LazyInitializationException。 使用 GraphQL,您可以指定要获取哪些字段,这样我就不想在未请求此类字段时进行不必要的联接(这里我们使用带有 DataLoader 的字段解析器)。

【问题讨论】:

理论上,使用FetchType.LAZY 应该已经使获取变得懒惰了。但在实践中,使用FetchType.LAZY 只是一个建议,JPA 可能仍会急切地加载Author 引用。 告诉您的团队不要指定任何内容,让 Hibernate 选择最佳模式。实际上,默认情况下,JPA \@ManyToOne 和 \@OneToOne 注释是 EAGERly 获取的,而 \@OneToMany 和 \@ManyToMany 关系被认为是 LAZY。这是默认策略 如果对您的问题有帮助,请查看此内容***.com/a/31715790/1460591 你无法避免他们获取新的数据库调用。假设您在各种关系中使用LAZY... 如果初级开发人员尝试访问休眠会话之外的某些相关对象(即在 JPA 事务之外),他们将收到错误但是...如果他们尝试访问在休眠会话(o JPA 事务)内部,休眠将尝试通过执行新调用来“填充”代理对象。恕我直言,最好的方法是在需要的地方使用LAZY 获取(也许默认设置就足够了)并检查代码质量以了解是否有一些不好的用法 @TacheDeChoco 但我不希望 ManyToOne 关系被急切地获取,并且即使在不需要它们时也不必要地进行额外的连接(例如,在使用 GraphQL 时,您可以指定要获取的字段)。并且有人仍然可能使用 OneToMany 关系执行 author.getBooks() 之类的操作并导致 N+1 问题。我要预防和防范所有此类问题。 【参考方案1】:

在不提及作者的情况下,使用 Book 实体的投影 (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) 是否足够?并且通过使用不同的投影来保证相关实体不会被无意加载?

【讨论】:

以上是关于使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?的主要内容,如果未能解决你的问题,请参考以下文章

JPA fetchType.Lazy 不工作

FetchType.Lazy 在 Spring Boot 中无法在 JPA 中工作

JPA 懒加载实践 fetch = FetchType.LAZY

Spring Boot 2. Jackson json 序列化或 Spring Data JPA FetchType.Lazy

FetchType.LAZY 不适用于@OneToOne 关系

hibernate FetchType理解