You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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;
}

代码细节解释

  1. 子查询部分:
    • 子查询的返回类型设为Long(匹配A的ID类型),精准获取我们需要的关联ID集合
    • 通过rootB.get("a").get("id")直接提取B关联的A的ID,对应原生SQL里的select b.idOfA from B b
  2. 主查询部分:
    • 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:09:41