Hibernate多对多关联子表带Predicate条件的Criteria查询问题
问题根因
- 现有代码将B表的过滤条件放在全局WHERE子句中,仅能过滤不存在符合条件B关联记录的A实体,不会对A内部携带的B关联列表做过滤。JPA默认加载关联集合时,不会继承主查询的WHERE条件作为关联集合的过滤规则。
- 代码未实现B到C表的关联逻辑,和你期望的SQL逻辑不符。
修复方案
使用JOIN FETCH + ON子句的方式,将过滤条件直接作用在关联的B、C集合上,而非全局WHERE子句。修改后的代码如下:
public Optional<A> method(String firstParam, String secondParam, Boolean thirdParam) { Preconditions.checkNotNull(firstParam); Preconditions.checkNotNull(secondParam); List<Predicate> predicates = new ArrayList<>(); CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<A> ACriteriaQuery = criteriaBuilder.createQuery(A.class); Root<A> ARoot = ACriteriaQuery.from(A.class); ACriteriaQuery.select(ARoot); // A表的过滤条件保留在WHERE子句 Predicate secondParamPredicate = criteriaBuilder.equal(ARoot.get("columnOne"), secondParam); Predicate firstParamPredicate = criteriaBuilder.equal(ARoot.get("columnTwo"), firstParam); predicates.add(secondParamPredicate); predicates.add(firstParamPredicate); if (thirdParam == null || thirdParam) { // 将普通LEFT JOIN改为LEFT JOIN FETCH,同时转为Join对象用于添加ON条件 Join<A, B> ABJoin = (Join<A, B>) ARoot.fetch("B", JoinType.LEFT); // 新增B到C的LEFT JOIN Join<B, C> BCJoin = ABJoin.join("C", JoinType.LEFT); // 关联表的过滤条件加到ON子句中,仅过滤关联集合,不过滤A实体 List<Predicate> joinPredicates = new ArrayList<>(); joinPredicates.add(criteriaBuilder.notEqual(BCJoin.get("column3"), "HELLO")); joinPredicates.add(criteriaBuilder.notEqual(BCJoin.get("column4"), "BYE")); ABJoin.on(joinPredicates.toArray(new Predicate[0])); } Predicate finalPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); ACriteriaQuery.where(finalPredicate).distinct(Boolean.TRUE); TypedQuery<A> query = entityManager.createQuery(ACriteriaQuery); List<A> As = query.getResultList(); if (As.isEmpty()) { return Optional.absent(); } if (As.size() > 1) { throw new NonUniqueResultException(); } return Optional.of(As.get(0)); }
补充说明
如果需求是同时过滤掉没有符合条件B关联记录的A实体,可以额外在全局WHERE子句中添加ABJoin.isNotNull()的谓词即可。
内容的提问来源于stack exchange,提问作者DeltaT
相关产品推荐
相关产品推荐

