如何用JPA2 Criteria Query查询未被Table B关联的Table A记录
修正JPA2 Criteria Query实现筛选未被B关联的A记录
咱们先梳理下原代码里的核心问题,再一步步修正到符合需求的状态:
原代码的关键错误点
- 子查询逻辑偏差:原代码查询了整个
B实体,但我们只需要B中关联A的ID值,不需要完整实体 - 查询选择器错误:
criteriaQueryA.select(A);这里应该传入查询根rootA,而非实体类本身 - 谓词逻辑与变量错误:不仅未定义的
rootAttemptA是笔误,且逻辑搞反了——我们需要的是A的ID不在子查询结果中,而非在结果中 - 子查询未正确提取关联ID:没有从
B的关联对象a中取出ID作为子查询的返回值
修正后的完整代码
public List<A> getANotInB() { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); // 1. 构建子查询:获取所有已被B关联的A的ID CriteriaQuery<Long> subQuery = criteriaBuilder.createQuery(Long.class); Root<B> rootB = subQuery.from(B.class); // 提取B中关联的A的ID作为子查询结果,对应数据库的a_id字段 subQuery.select(rootB.get("a").get("id")); // 2. 构建主查询:从A中筛选ID不在子查询结果里的记录 CriteriaQuery<A> criteriaQueryA = criteriaBuilder.createQuery(A.class); Root<A> rootA = criteriaQueryA.from(A.class); criteriaQueryA.select(rootA); // 正确指定查询返回的A实体 // 3. 构建NOT IN谓词 Predicate predicate = rootA.get("id").in(subQuery); criteriaQueryA.where(criteriaBuilder.not(predicate)); // 执行查询并返回结果 TypedQuery<A> query = entityManager.createQuery(criteriaQueryA); List<A> as = query.getResultList(); System.out.println(as); return as; }
代码细节解释
- 子查询部分:
- 子查询的返回类型设为
Long(匹配A的ID类型),精准获取我们需要的关联ID集合 - 通过
rootB.get("a").get("id")直接提取B关联的A的ID,对应原生SQL里的select b.idOfA from B b
- 子查询的返回类型设为
- 主查询部分:
- 用
rootA.get("id").in(subQuery)先判断A的ID是否在子查询结果中,再通过criteriaBuilder.not()取反,完美对应原生SQL的not in逻辑 - 用
criteriaQueryA.select(rootA)正确指定查询返回的是A实体对象
- 用
可选优化:类型安全写法
如果你的项目生成了JPA静态元模型(可通过Hibernate JPA Model Generator工具生成),可以用类型安全的方式访问属性,避免硬编码字段名的拼写错误:
// 子查询提取ID的类型安全写法 subQuery.select(rootB.get(B_.a).get(A_.id)); // 主查询构建谓词的类型安全写法 Predicate predicate = rootA.get(A_.id).in(subQuery);
内容的提问来源于stack exchange,提问作者StrikerVillain
相关产品推荐
相关产品推荐

