休眠连接获取一对多的多个表

问题描述

我的数据模型中有以下实体关系。

ERD

Entity A: one-to-many :Entity B

Entity B: one-to-many :Entity C

Entity B: one-to-many :Entity D

休眠实体:

public class EntityA {
    
    private Integer id;
    
    @OnetoMany
    private List<EntityB> entityBList;
}

public class EntityB {
    
    private Integer id;

    @ManyToOne
    private EntityA entityA;
    
    @OnetoMany(fetch=FetchType.LAZY)
    private List<EntityC> entityCList;

    @OnetoMany(fetch=FetchType.LAZY)
    private List<EntityD> entityDList;
}

public class EntityC {
    
    private Integer id;
    
    @ManyToOne
    private EntityB entityB;
}

public class EntityD {

    private Integer id;

    @ManyToOne
    private EntityB entityB;
}

要求:我想通过对实体 B 的连接获取查询实体 C,同时也急切地获取实体 D。查询完成后,我希望如果我做entityC.getEntityB().getEntityDList(),它应该不会导致休眠中的N+1查询问题。

我正在尝试以下 JPQL 查询

select ec from EntityC ec 
join fetch ec.entityB eb 
join fetch eb.entityD ed 
where ec.id = :id

由于与实体 D 的交叉连接,这会导致结果重复。而不是一个结果,我得到了 n 个结果,其中 n 是实体 D 的列表大小。

我怎样才能避免这种情况?有没有什么办法可以在 JPQL 中不交叉连接的情况下获取实体 D?

解决方法

第一件事是在您的查询中使用 DISTINCT JPQL 关键字,例如如:

TypedQuery<EntityC> query = em.createQuery("SELECT DISTINCT ec FROM EntityC ec JOIN FETCH ec.entityB eb JOIN FETCH eb.entityDList ed WHERE ec.id = :id",EntityC.class);
//                                                 ^^^^^^^^

这将消除重复项,但也有将 DISTINCT 传递给 SQL 查询的副作用,这不是您想要的。阅读详细信息 hereherethis 优秀答案。长话短说 - 引用 Vlad Mihalcea 的 answer

通过将 DISTINCT 传递给 SQL 查询,EXECUTION PLAN 将执行一个额外的排序阶段,这会增加开销而不带来任何值[...]

解决方案在链接的文章和答案中,但简而言之,如果您使用的是 Hibernate >= 5.2.2,请使用 hibernate.query.passDistinctThrough 提示:

TypedQuery<EntityC> query = em.createQuery("SELECT DISTINCT ec FROM EntityC ec JOIN FETCH ec.entityB eb JOIN FETCH eb.entityDList ed WHERE ec.id = :id",EntityC.class);
query.setHint("hibernate.query.passDistinctThrough",false);
// -OR-
query.setHint(QueryHints.HINT_PASS_DISTINCT_THROUGH,false); // import org.hibernate.jpa.QueryHints