【问题标题】:hibernate generate query without all outer join fetch possible休眠生成查询,没有所有外部连接获取可能
【发布时间】:2015-08-25 13:16:26
【问题描述】:

我有 5 个实体:

@Entity
public class A {
    @OneToMany(mappedBy="a", fetch=FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private Set<B1> b1s;

    @OneToMany(mappedBy="a", fetch=FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private Set<B2> b2s;
}

@Entity
public class B1 {
    @ManyToOne
    @JoinColumn(name="c")
    private C c;
}

@Entity
public class B2 {
    @ManyToOne
    @JoinColumn(name="c")
    private C c;
}

@Entity
public class C {
    @OneToMany(mappedBy="d", fetch=FetchType.EAGER)
    @Fetch(FetchMode.JOIN)
    private Set<D> ds;
}

@Entity
public classD {

}

总之, 一个渴望加入两组实体 B1 和 B2,每个都渴望加入(隐式)一个实体 C,而后者又渴望加入一组实体 D。

当加载一个 A 对象时,生成的查询看起来像

select (...) from A a0 
left outer join B1 b1 on a0.id=b1.aid
    left outer join C c1 on b1.cid=c1.id
        left outer join D d1 on c1.id=d1.cid
left outer join B2 b2 on a0.id=b2.aid
    left outer join C c2 on b2.cid=c2.id
where a0.id=?

问题是与 c2 链接的实体 D 集(通过 B2 实体加载的 C 实体)未在同一个查询中加载,导致每个 c2 对象有 N 个后续查询。

我希望 Hibernate 在第一次查询中为这些对象生成另一个左外连接,就像它在第一次出现 D 时已经这样做了。

我错过了什么?我在 Oracle DB 上使用休眠 3.6,这是一个已知问题吗?

感谢您的宝贵时间。

【问题讨论】:

    标签: java hibernate join


    【解决方案1】:

    确实,这似乎是一个错误或不完整的功能。我也能够在更简单的场景中重现它:

    @Entity
    public class A {
        @OneToMany(fetch=FetchType.EAGER)
        @Fetch(FetchMode.JOIN)
        @JoinColumn
        private Set<B> bs1;
    
        @OneToMany(fetch=FetchType.EAGER)
        @Fetch(FetchMode.JOIN)
        @JoinColumn
        private Set<B> bs2;
    }
    
    @Entity
    public class B {
        @OneToMany(fetch=FetchType.EAGER)
        @Fetch(FetchMode.JOIN)
        @JoinColumn
        private Set<C> cs;
    }
    
    @Entity
    public class C {
    
    }
    

    当通过id加载A时,会生成如下join:

    from
        A a0_ 
    left outer join
        B bs1x1_ 
            on a0_.id=bs1x1_.bs1_id 
    left outer join
        C cs2_ 
            on bs1x1_.id=cs2_.cs_id 
    left outer join
        B bs2x3_ 
            on a0_.id=bs2x3_.bs2_id 
    

    因此,如果您真的想在一个查询中获取所有内容,则必须使用 HQL:

    select a from A a 
    left join fetch a.b1s b1 
    left join fetch b1.c c1 
    left join fetch c1.ds 
    left join fetch a.b2s b2 
    left join fetch b2.c c2 
    left join fetch c2.ds 
    where a.id = ?
    

    但是,我认为要加入的集合非常小。因为这些类型的连接会产生 full 笛卡尔积并且被认为是非常糟糕的反模式。

    假设每个集合仅包含 100 行(A 有 100 个 b1s 和 100 个b2sb1sb2s 中的每一个都有一个 C 有 100 个 ds) .然后您将获得并读取包含 1 亿行的结果集!

    但以惰性集合和批量提取为例,您将在几个查询中读取总共大约 400 行,这比在一个查询中读取 1 亿行要快得多。

    【讨论】:

    • 查询一个A返回的行数比较少。我的问题是 A 是批量获取的,所以我不能使用 HQL 加载它,也不能在原始 HQL 查询中查询 A 及其子实体,因为它会返回太多(大)行
    猜你喜欢
    • 1970-01-01
    • 2013-01-19
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    相关资源
    最近更新 更多