Hibernate标准OneToOne FetchType.Eager FetchMode.Join执行不必要的查询

时间:2017-05-05 07:33:03

标签: java hibernate hibernate-criteria criteria-api

我有示例实体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触发另一个选择的原因是什么?

1 个答案:

答案 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个选择。