了解 Hibernate/JPA 对 findAll 和 findOne 的不同行为
Understand different behaviours of Hibernate/JPA for findAll and findOne
我正在使用 findAll 方法(来自 Spring Data JPA Repository)调试性能问题,问题是正在使用的获取计划。 (Hibernate 正在执行额外的选择来检索额外的实体,而不是使用提取连接)
我的实体与另一个实体有一些关系,我面临的问题与 *ToOne 关系有关。
在 google 上花了一些时间后,我意识到有一些方法可以管理 Hibernate 将执行的查询(使用 JPA Criteria、NamedEntityGraph、自定义查询,...)。
我创建了一个小示例来演示不同的行为,但我想了解为什么 Hibernate 默认情况下这样做。我花了一些时间在文档中搜索,但找不到任何对此默认行为的引用。
https://github.com/pmvilaca/jpa-test
区别:
Hibernate:
SELECT
user0_.id AS id1_1_0_,
userdetail1_.id AS id1_21_,
contactdet2_.id AS id1_0_2_,
user0_.contact_details_id AS contact_3_1_0_,
user0_.name AS name2_1_0_,
userdetail1_.nationality AS national2_2_1_,
userdetail1_.user_id AS user_id3_2_1_,
contactdet2_.email AS email2_0_2_,
contactdet2_.mobile AS mobile3_0_2_
FROM user user0_
LEFT OUTER JOIN user_details userdetail1_ ON user0_.id = userdetail1_.user_id
LEFT OUTER JOIN contact_details contactdet2_ ON user0_.contact_details_id = contactdet2_.id
和
Hibernate: select user0_.id as id1_1_, user0_.contact_details_id as contact_3_1_, user0_.name as name2_1_ from user user0_
Hibernate: select contactdet0_.id as id1_0_0_, contactdet0_.email as email2_0_0_, contactdet0_.mobile as mobile3_0_0_ from contact_details contactdet0_ where contactdet0_.id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
Hibernate: select contactdet0_.id as id1_0_0_, contactdet0_.email as email2_0_0_, contactdet0_.mobile as mobile3_0_0_ from contact_details contactdet0_ where contactdet0_.id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
有什么想法吗?
谢谢
@OneToOne
的默认提取类型是 FetchType.EAGER。
因此,在没有任何关于如何优化查询的提示的情况下,Hibernate 将遵循以下步骤:
Select 所有 User
的:
Hibernate:
select
user0_.id as id1_1_,
user0_.contact_details_id as contact_3_1_,
user0_.name as name2_1_
from
user user0_
现在急切加载每个User
的ContactDetails
和UserDetails
Hibernate:
select
contactdet0_.id as id1_0_0_,
contactdet0_.email as email2_0_0_,
contactdet0_.mobile as mobile3_0_0_
from
contact_details contactdet0_
where
contactdet0_.id=?
Hibernate:
select
userdetail0_.id as id1_2_2_,
userdetail0_.nationality as national2_2_2_,
userdetail0_.user_id as user_id3_2_2_,
user1_.id as id1_1_0_,
user1_.contact_details_id as contact_3_1_0_,
user1_.name as name2_1_0_,
contactdet2_.id as id1_0_1_,
contactdet2_.email as email2_0_1_,
contactdet2_.mobile as mobile3_0_1_
from
user_details userdetail0_
left outer join
user user1_
on userdetail0_.user_id=user1_.id
left outer join
contact_details contactdet2_
on user1_.contact_details_id=contactdet2_.id
where
userdetail0_.user_id=?
...
这通常称为 n + 1
问题。例如,参见 here。
我正在使用 findAll 方法(来自 Spring Data JPA Repository)调试性能问题,问题是正在使用的获取计划。 (Hibernate 正在执行额外的选择来检索额外的实体,而不是使用提取连接)
我的实体与另一个实体有一些关系,我面临的问题与 *ToOne 关系有关。
在 google 上花了一些时间后,我意识到有一些方法可以管理 Hibernate 将执行的查询(使用 JPA Criteria、NamedEntityGraph、自定义查询,...)。
我创建了一个小示例来演示不同的行为,但我想了解为什么 Hibernate 默认情况下这样做。我花了一些时间在文档中搜索,但找不到任何对此默认行为的引用。
https://github.com/pmvilaca/jpa-test
区别:
Hibernate:
SELECT
user0_.id AS id1_1_0_,
userdetail1_.id AS id1_21_,
contactdet2_.id AS id1_0_2_,
user0_.contact_details_id AS contact_3_1_0_,
user0_.name AS name2_1_0_,
userdetail1_.nationality AS national2_2_1_,
userdetail1_.user_id AS user_id3_2_1_,
contactdet2_.email AS email2_0_2_,
contactdet2_.mobile AS mobile3_0_2_
FROM user user0_
LEFT OUTER JOIN user_details userdetail1_ ON user0_.id = userdetail1_.user_id
LEFT OUTER JOIN contact_details contactdet2_ ON user0_.contact_details_id = contactdet2_.id
和
Hibernate: select user0_.id as id1_1_, user0_.contact_details_id as contact_3_1_, user0_.name as name2_1_ from user user0_
Hibernate: select contactdet0_.id as id1_0_0_, contactdet0_.email as email2_0_0_, contactdet0_.mobile as mobile3_0_0_ from contact_details contactdet0_ where contactdet0_.id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
Hibernate: select contactdet0_.id as id1_0_0_, contactdet0_.email as email2_0_0_, contactdet0_.mobile as mobile3_0_0_ from contact_details contactdet0_ where contactdet0_.id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
Hibernate: select userdetail0_.id as id1_2_2_, userdetail0_.nationality as national2_2_2_, userdetail0_.user_id as user_id3_2_2_, user1_.id as id1_1_0_, user1_.contact_details_id as contact_3_1_0_, user1_.name as name2_1_0_, contactdet2_.id as id1_0_1_, contactdet2_.email as email2_0_1_, contactdet2_.mobile as mobile3_0_1_ from user_details userdetail0_ left outer join user user1_ on userdetail0_.user_id=user1_.id left outer join contact_details contactdet2_ on user1_.contact_details_id=contactdet2_.id where userdetail0_.user_id=?
有什么想法吗?
谢谢
@OneToOne
的默认提取类型是 FetchType.EAGER。
因此,在没有任何关于如何优化查询的提示的情况下,Hibernate 将遵循以下步骤:
Select 所有 User
的:
Hibernate:
select
user0_.id as id1_1_,
user0_.contact_details_id as contact_3_1_,
user0_.name as name2_1_
from
user user0_
现在急切加载每个User
的ContactDetails
和UserDetails
Hibernate:
select
contactdet0_.id as id1_0_0_,
contactdet0_.email as email2_0_0_,
contactdet0_.mobile as mobile3_0_0_
from
contact_details contactdet0_
where
contactdet0_.id=?
Hibernate:
select
userdetail0_.id as id1_2_2_,
userdetail0_.nationality as national2_2_2_,
userdetail0_.user_id as user_id3_2_2_,
user1_.id as id1_1_0_,
user1_.contact_details_id as contact_3_1_0_,
user1_.name as name2_1_0_,
contactdet2_.id as id1_0_1_,
contactdet2_.email as email2_0_1_,
contactdet2_.mobile as mobile3_0_1_
from
user_details userdetail0_
left outer join
user user1_
on userdetail0_.user_id=user1_.id
left outer join
contact_details contactdet2_
on user1_.contact_details_id=contactdet2_.id
where
userdetail0_.user_id=?
...
这通常称为 n + 1
问题。例如,参见 here。