为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系

Posted

技术标签:

【中文标题】为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系【英文标题】:Why does JPA use FetchType EAGER by default for the @ManyToOne relationship为什么 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系 【发布时间】:2015-02-15 15:15:43 【问题描述】:

我注意到他对于 @ManyToOne 映射的默认 FetchTypeEAGER i JPA 和 Hibernate,而对于 @OneToMany 映射,默认 FetchTypeLAZY

这背后的具体原因是什么?

【问题讨论】:

【参考方案1】:

JPA 2.0 规范c,默认值如下:

OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER

而在 hibernate 中,一切都是懒惰的

来自 Hibernate 文档,

默认情况下,Hibernate 对集合使用惰性选择获取和 单值关联的惰性代理获取。这些默认值 对大多数应用程序中的大多数关联都有意义。

为了回答您的问题,Hibernate 是 JPA 标准的一种实现。 Hibernate 有自己的操作怪癖,但根据 Hibernate 文档

By default, Hibernate uses lazy select fetching for collections and lazy proxy fetching for single-valued associations. These defaults make sense for most associations in the majority of applications.

因此,无论您声明了何种类型的关系,Hibernate 将始终使用延迟获取策略加载任何对象。

JPA 规范 假设通常大多数应用程序默认情况下需要单例关系是急切的,而默认情况下多值关系是惰性的。

更多信息请参考here

【讨论】:

您能否提供您复制引用文本的文档的链接? “所以 Hibernate 将始终使用惰性获取策略加载任何对象,无论您声明了哪种类型的关系。”不正确,因为 Hibernate 在我使用 ManyToOne 映射的代码中使用 EAGER 策略加载。而且在休眠源默认获取类型设置为 EAGER。 @vatsalmevada 你使用的是 Hibernate 还是 JPA 对不起,我正在使用 JPA 注释。如果我使用休眠映射文件,那么默认情况下它会执行 LAZY fetch 吗? @vatsalmevada 是的,这就是我在回答中所解释的,JPA 和 Hibernate 的行为不同。 JPA 只是实现【参考方案2】:

JPA 陷阱

将这些设置为 EAGER 的原因是因为早期的 JPA 1.0 设计者认为强制 JPA 实现以支持动态初始化代理将是一个非常强烈的要求。但是,由于没有代理,性能会受到极大影响,因此所有提供商都支持 LAZY 关联。

避免FetchType.EAGER

使用 @ManyToOne@OneToOne 关联的默认 EAGER 获取策略是 a terrible idea,因为您可以轻松地以 N+1 query issues 结束。

使用 Hibernate 时,一旦关联设置为 FetchType.EAGER,您就不能再在查询时懒惰地获取它。因此,无论当前的业务用例是否需要,您总是要获取该关系。

默认使用FetchType.LAZY

因此,您最好对所有关联默认使用FetchType.LAZY

FetchType.EAGER 不同,FetchType.LAZY 关系可以在查询时使用JOIN FETCH 子句急切地获取。

您唯一需要注意的是,如果您需要在 JPA EntityManager 关闭后访问关联,则需要在当前运行的持久性上下文的上下文中获取惰性关联。否则,您将获得LazyInitializationException

【讨论】:

另外值得注意的是,Hibernate 中的@OneToOne 始终是 Eager,除非关联被标记为 optional=false。 ***.com/questions/1444227/… 除了许多 JPA 提供者不使用“代理”来处理单值关系。【参考方案3】:

在 Hibernate OneToOne 和 ManyToOne 中,默认情况下不会是惰性的,即使您明确将其设置为惰性也是如此。

示例:有一个关联 Parent->Child。默认情况下,这意味着PARENT_ID 列在CHILD 表中。 ORM 不知道您的 Parent 是否真的有 Child 或者它是否为 null。如果有 Child,则必须将对象设置为 Parent 的字段。如果它为空,那么 ORM 必须设置为空。要确定它是否为空 ORM 必须查询 CHILD 表。由于无论如何都会进行查询,因此如果 Child 存在,则立即返回它是有意义的。

因此,要使 MTO 或 OTO 变得懒惰,您需要指示 ORM Child 始终存在(通过使其不是可选的,也就是必需的,也就是 not-null)。因此 ORM 知道它始终存在并且知道它可以设置代理而不是 Child。

或者,您可以将 CHILD_ID 列保留在 PARENT 表中。然后,如果 Child 为 null,则记录中的值也将为 null。但为此您需要添加配置选项(如@JoinColumn),这不是默认设置。

【讨论】:

【参考方案4】:

如果您在 ManyToOne 中使用lazyfetch,则在执行查询时必须使用 join 来获取多端中的所有模型

【讨论】:

【参考方案5】:

如果你仔细看,你会发现如果关系以Many关键字结尾,即OneToManyManyToMany,就是Lazy。如果它以One 结尾,即ManyToOneOneToOne,它就是Eager。所以问题是如果你只需要加载一个对象,它会非常快地获取它。但是,如果它要加载许多对象,则将花费大量时间。因此,要在默认情况下停止加载时间,他们应该将其设置为延迟加载。

【讨论】:

以上是关于为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系的主要内容,如果未能解决你的问题,请参考以下文章

使用 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

Hibernate:为啥 FetchType.LAZY 注释的集合属性急切地加载?