问题描述
我的数据模型中有以下实体关系。
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 查询的副作用,这不是您想要的。阅读详细信息 here、here 和 this 优秀答案。长话短说 - 引用 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