为啥 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
映射的默认 FetchType
是 EAGER
i JPA 和 Hibernate,而对于 @OneToMany
映射,默认 FetchType
是 LAZY
。
这背后的具体原因是什么?
【问题讨论】:
【参考方案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
关键字结尾,即OneToMany
,ManyToMany
,就是Lazy。如果它以One
结尾,即ManyToOne
、OneToOne
,它就是Eager。所以问题是如果你只需要加载一个对象,它会非常快地获取它。但是,如果它要加载许多对象,则将花费大量时间。因此,要在默认情况下停止加载时间,他们应该将其设置为延迟加载。
【讨论】:
以上是关于为啥 JPA 默认使用 FetchType EAGER 来处理 @ManyToOne 关系的主要内容,如果未能解决你的问题,请参考以下文章
使用 FetchType.LAZY 防止 JPA/Hibernate 中的延迟加载(尤其是在使用 @Transactional 时)?
FetchType.Lazy 在 Spring Boot 中无法在 JPA 中工作
JPA 懒加载实践 fetch = FetchType.LAZY
Spring Boot 2. Jackson json 序列化或 Spring Data JPA FetchType.Lazy