JPA 条件按相关实体过滤而不加入

JPA criteria filter by related entity without join

我想知道如何使用 JPA Criteria API 通过相关实体的外键过滤实体。

假设我有两个实体如下:

public class Employee {
    @Id
    private Long id;
    ...
    @ManyToOne
    private Department department;
    ...
}

public class Department {
    @Id
    private Long id;
}

我想查询id为(1,2,3)的部门下的员工

我能够使用 Hibernate 的 depricated 标准来做到这一点,并且想知道如何使用 JPA 标准谓词 without join (root.join) 来做到这一点。我不需要任何连接或子查询是合乎逻辑的,因为可以从 table:

中获取所需的结果
select e.* from employee e where e.department_id in (1,2,3)

** 更新 **

我的问题是 - 作为 JPA 标准的新问题并且来自已弃用的 Hibernate 标准 - 我已经使用了 CriteriaBuilder 中的所有 APIs,例如(等于,notEqual,isNull,例如,.. ...);因此,使用了 CriteriaBuilder.In(experssion).in(values)。 但是,正如@frank 的回答所示,我发现对于 IN 用法,我将使用 Root.get(<attr>).in(<values>)

CriteriaBuilder.in也可以使用,但不同的是:

In<Object> inClause = criteriaBuilder.in(root.get(<attr>);
for (Long id : ids) {
    inClause.value(id);
}

但是,当然,第一个解决方案更简单。

这在 SQL 中是合乎逻辑的,但在 JPA 中,您应该执行 Join 以防止启动查询以初始化 Department 类型对象。

可以做到,但我觉得用Join做会更好

最后,即使 table 有信息,Criteria-api 理解的 JPA 实体也没有那个 ID,它有一个对象的引用编号.

虽然我不推荐它,但实现会是这样的:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);

Root<Employee> root = cq.from(Employee.class);

cq.where(root.get("department").in(listIds));
cq.select(root);

激活日志跟踪以显示已启动的查询,并考虑是否值得抛出一个带连接的查询或 N 个不带连接的查询。

为了不使内存过载,您应该建立惰性关系。

Set<Integer> departments = new HashSet<Integer>();
departments.add(1);
departments.add(2);
departments.add(3);

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> r = cq.from(Employee.class);
cq.select(r).where(r.get(Employee_.department).get(Department_.id).in(departments));
TypedQuery<Employee> query = em.createQuery(cq);
List<Employee> result = query.getResultList();