我有示例实体TableA和TableB,在表A中我们将列TABLE_B_ID注释为如下:
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "TABLE_B_ID")
@Fetch(FetchMode.JOIN)
然后当我执行这样的简单代码时:
Criteria c = session.createCriteria(TableA.class);
c.list();
Hibernate运行查询n + 1次,其中n = TableA中的行数,即使TableA已经在第一个查询中加入TableB,Hibernate仍然试图通过另一个选择查询为每个A获取TableB。
查询已解雇:
Hibernate:
select
this_.id as id1_0_1_,
this_.name as name2_0_1_,
this_.TABLE_B_ID as TABLE_B_3_0_1_,
tableb2_.id as id1_1_0_,
tableb2_.name as name2_1_0_
from
TABLE_A this_
inner join
TABLE_B tableb2_
on this_.TABLE_B_ID=tableb2_.id
Hibernate:
select
tablea0_.id as id1_0_1_,
tablea0_.name as name2_0_1_,
tablea0_.TABLE_B_ID as TABLE_B_3_0_1_,
tableb1_.id as id1_1_0_,
tableb1_.name as name2_1_0_
from
TABLE_A tablea0_
inner join
TABLE_B tableb1_
on tablea0_.TABLE_B_ID=tableb1_.id
where
tablea0_.TABLE_B_ID=?
Hibernate:
select
tablea0_.id as id1_0_1_,
tablea0_.name as name2_0_1_,
tablea0_.TABLE_B_ID as TABLE_B_3_0_1_,
tableb1_.id as id1_1_0_,
tableb1_.name as name2_1_0_
from
TABLE_A tablea0_
inner join
TABLE_B tableb1_
on tablea0_.TABLE_B_ID=tableb1_.id
where
tablea0_.TABLE_B_ID=?
依旧......
我已尝试删除FetchMode
或更改optional=true
,但触发的查询仍为n + 1次。
即使他已经获得了第一个选择查询的数据,hibernate触发另一个选择的原因是什么?
答案 0 :(得分:0)
您正在遇到一个众所周知的问题," N + 1选择"选择父实体时会出现问题,而hibernate将使用OneToOne为与父级相关的子项进行额外选择。所以如果你有" N"在数据库中的父子记录,hibernate将获得所有父母一个选择,然后让每个孩子分开选择,使得总N + 1选择。 " N + 1"有两种方法。 hibernate中的问题: 1."加入Fetch"所有OneToOne儿童。 2.启用二级缓存并在OneToOne子级上使用@Cache批注。
你的问题是你没有加入fetch"所有的OneToOne孩子。你必须"加入fetch"它们全部,包括传递子(从孩子自己或在集合中引用的实体)。
使OneToOne懒惰(因为它默认情况下急切)只是部分解决方案,因为只有当你访问孩子的某个getter时,hibernate才会为孩子做一个选择,但从长远来看,它仍会进行所有N个选择。