JPA 和 Hibernate:预加载执行后续查询以获取所有数据,而不是只在一个查询中执行
JPA & Hibernate: Eager loading performing subsequent queries to fetch all data, instead of doing it in just one query
我有以下疑问。我想知道为什么在使用 JPA 和 Hibernate 时,在 ManyToOne 或 OneToMany 关系中执行预加载时,它会按顺序调用数据库获取实体信息,但另外,生成后续查询以获取每个子项。
另一方面,当使用带有 JOIN FETCH 的查询时,它会按我预期的那样执行查询,同时获取所有信息,因为 fetchType 表示为 "EAGER"。
这是一个简单的例子:
我有 Class 学生,它与 Class Class 房间有 ManyToOne 关系。
@Entity
@Table(name = "STUDENT")
public class Student {
@ManyToOne(optional = true, fetch = FetchType.EAGER)
@JoinColumn(name = "ClassroomID")
private Classroom mainClass;
另一边有class名为Class的房间如下:
@Entity
public class Classroom {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "mainClass", fetch = FetchType.EAGER)
private List<Student> studentsList;
在获取Classroom对象时,先查询一次获取自己的信息,再查询一次获取studentsList中每个学生的信息对于每个 classRoom 对象。
第一次查询:
Hibernate:
/* SELECT
r
FROM
Classroom r
LEFT JOIN
r.classStudents */
select
classroom0_.id as id1_0_,
classroom0_.number as number2_0_
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
然后它执行下一个查询的次数与分配给每个 class 房间的学生一样多。
Hibernate:
/* load one-to-many com.hw.access.Classroom.classStudents */
select
classstude0_.ClassroomID as Classroo4_0_1_,
classstude0_.id as id1_1_1_,
classstude0_.id as id1_1_0_,
classstude0_.FIRST_NAME as FIRST_NA2_1_0_,
classstude0_.LAST_NAME as LAST_NAM3_1_0_,
classstude0_.ClassroomID as Classroo4_1_0_
from
STUDENT classstude0_
where
classstude0_.ClassroomID=?
问题是:为什么不一次获取所有信息?为什么它不在一次查询中获取信息?因为它已经在那里执行 Join 子句.
为什么只在查询中显式添加 Fetch 时,它会按要求执行?
例如:
SELECT
r
FROM
Classroom r
LEFT JOIN FETCH
r.classStudents */
然后,输出查询确实在一个查询中获取了所有信息:
Hibernate:
select
classroom0_.id as id1_0_0_,
classstude1_.id as id1_1_1_,
classroom0_.number as number2_0_0_,
classstude1_.FIRST_NAME as FIRST_NA2_1_1_,
classstude1_.LAST_NAME as LAST_NAM3_1_1_,
classstude1_.ClassroomID as Classroo4_1_1_,
classstude1_.ClassroomID as Classroo4_0_0__,
classstude1_.id as id1_1_0__
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
默认情况下,fetchtype 是惰性的,这意味着如果您不在请求中请求列表,Hibernate 将不会收集它。
在第一个请求中,您要求提供 Classroom r 的所有属性,包括学生列表,因此 Hibernate 将延迟加载它们(在发现您需要它们之后)。
但是当 fetchtype 设置为 eager hibernate 时,即使你不询问它也会收集它。
因为您有从 Classroom
到 Student
的 OneToMany
关系,使用单个查询会导致每一行重复 Classroom
字段。
现在假设你有第二个 OneToMany
从 Classroom
到 Course
的关系;如果对于给定的 Classroom
,您有 N Student
和 M Course
,您将有一个查询返回 N+M 行,每行包含 class 的相同字段Classroom
.
我发现它在 https://vladmihalcea.com/eager-fetching-is-a-code-smell/ 中有描述
在 EAGER 下获取不一致:
Both JPQL and Criteria queries default to select fetching, therefore issuing a secondary select for each individual EAGER association. The larger the associations’ number, the more additional individual SELECTS, the more it will affect our application performance.
另外,请注意 Hibernate 同样会忽略 HQL 查询的提取注释:
https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects
Hibernate ignores my outer-join="true" or fetch="join" setting and fetches an association lazily, using n+1 selects!
HQL queries always ignore the setting for outer-join or fetch="join" defined in mapping metadata. This setting applies only to associations fetched using get() or load(), Criteria queries, and graph navigation. If you need to enable eager fetching for a HQL query, use an explicit LEFT JOIN FETCH.
我有以下疑问。我想知道为什么在使用 JPA 和 Hibernate 时,在 ManyToOne 或 OneToMany 关系中执行预加载时,它会按顺序调用数据库获取实体信息,但另外,生成后续查询以获取每个子项。
另一方面,当使用带有 JOIN FETCH 的查询时,它会按我预期的那样执行查询,同时获取所有信息,因为 fetchType 表示为 "EAGER"。
这是一个简单的例子:
我有 Class 学生,它与 Class Class 房间有 ManyToOne 关系。
@Entity
@Table(name = "STUDENT")
public class Student {
@ManyToOne(optional = true, fetch = FetchType.EAGER)
@JoinColumn(name = "ClassroomID")
private Classroom mainClass;
另一边有class名为Class的房间如下:
@Entity
public class Classroom {
@OneToMany(cascade = CascadeType.ALL, mappedBy = "mainClass", fetch = FetchType.EAGER)
private List<Student> studentsList;
在获取Classroom对象时,先查询一次获取自己的信息,再查询一次获取studentsList中每个学生的信息对于每个 classRoom 对象。
第一次查询:
Hibernate:
/* SELECT
r
FROM
Classroom r
LEFT JOIN
r.classStudents */
select
classroom0_.id as id1_0_,
classroom0_.number as number2_0_
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
然后它执行下一个查询的次数与分配给每个 class 房间的学生一样多。
Hibernate:
/* load one-to-many com.hw.access.Classroom.classStudents */
select
classstude0_.ClassroomID as Classroo4_0_1_,
classstude0_.id as id1_1_1_,
classstude0_.id as id1_1_0_,
classstude0_.FIRST_NAME as FIRST_NA2_1_0_,
classstude0_.LAST_NAME as LAST_NAM3_1_0_,
classstude0_.ClassroomID as Classroo4_1_0_
from
STUDENT classstude0_
where
classstude0_.ClassroomID=?
问题是:为什么不一次获取所有信息?为什么它不在一次查询中获取信息?因为它已经在那里执行 Join 子句.
为什么只在查询中显式添加 Fetch 时,它会按要求执行?
例如:
SELECT
r
FROM
Classroom r
LEFT JOIN FETCH
r.classStudents */
然后,输出查询确实在一个查询中获取了所有信息:
Hibernate:
select
classroom0_.id as id1_0_0_,
classstude1_.id as id1_1_1_,
classroom0_.number as number2_0_0_,
classstude1_.FIRST_NAME as FIRST_NA2_1_1_,
classstude1_.LAST_NAME as LAST_NAM3_1_1_,
classstude1_.ClassroomID as Classroo4_1_1_,
classstude1_.ClassroomID as Classroo4_0_0__,
classstude1_.id as id1_1_0__
from
Classroom classroom0_
left outer join
STUDENT classstude1_
on classroom0_.id=classstude1_.ClassroomID
默认情况下,fetchtype 是惰性的,这意味着如果您不在请求中请求列表,Hibernate 将不会收集它。
在第一个请求中,您要求提供 Classroom r 的所有属性,包括学生列表,因此 Hibernate 将延迟加载它们(在发现您需要它们之后)。
但是当 fetchtype 设置为 eager hibernate 时,即使你不询问它也会收集它。
因为您有从 Classroom
到 Student
的 OneToMany
关系,使用单个查询会导致每一行重复 Classroom
字段。
现在假设你有第二个 OneToMany
从 Classroom
到 Course
的关系;如果对于给定的 Classroom
,您有 N Student
和 M Course
,您将有一个查询返回 N+M 行,每行包含 class 的相同字段Classroom
.
我发现它在 https://vladmihalcea.com/eager-fetching-is-a-code-smell/ 中有描述 在 EAGER 下获取不一致:
Both JPQL and Criteria queries default to select fetching, therefore issuing a secondary select for each individual EAGER association. The larger the associations’ number, the more additional individual SELECTS, the more it will affect our application performance.
另外,请注意 Hibernate 同样会忽略 HQL 查询的提取注释: https://developer.jboss.org/wiki/HibernateFAQ-AdvancedProblems#jive_content_id_Hibernate_ignores_my_outerjointrue_or_fetchjoin_setting_and_fetches_an_association_lazily_using_n1_selects
Hibernate ignores my outer-join="true" or fetch="join" setting and fetches an association lazily, using n+1 selects!
HQL queries always ignore the setting for outer-join or fetch="join" defined in mapping metadata. This setting applies only to associations fetched using get() or load(), Criteria queries, and graph navigation. If you need to enable eager fetching for a HQL query, use an explicit LEFT JOIN FETCH.