【发布时间】:2015-02-15 15:15:43
【问题描述】:
我注意到他对于 @ManyToOne 映射的默认 FetchType 是 EAGER i JPA 和 Hibernate,而对于 @OneToMany 映射,默认 FetchType 是 LAZY。
这背后的具体原因是什么?
【问题讨论】:
标签: java hibernate jpa orm hibernate-mapping
我注意到他对于 @ManyToOne 映射的默认 FetchType 是 EAGER i JPA 和 Hibernate,而对于 @OneToMany 映射,默认 FetchType 是 LAZY。
这背后的具体原因是什么?
【问题讨论】:
标签: java hibernate jpa orm hibernate-mapping
从 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
【讨论】:
将这些设置为 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 和 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),这不是默认设置。
【讨论】:
如果您在 ManyToOne 中使用lazyfetch,则在执行查询时必须使用 join 来获取多端中的所有模型
【讨论】:
如果你仔细看,你会发现如果关系以Many关键字结尾,即OneToMany,ManyToMany,就是Lazy。如果它以One 结尾,即ManyToOne、OneToOne,它就是Eager。所以问题是如果你只需要加载一个对象,它会非常快地获取它。但是,如果它要加载许多对象,则将花费大量时间。因此,要在默认情况下停止加载时间,他们应该将其设置为延迟加载。
【讨论】: