【问题标题】:JPA criteria filter by related entity without joinJPA 标准按相关实体过滤,无需连接
【发布时间】:2020-12-16 20:18:03
【问题描述】:

我想知道如何使用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 的废弃标准来做到这一点,并且想知道如何使用 JPA 标准谓词 without join (root.join) 来做到这一点。我不需要任何连接或子查询是合乎逻辑的,因为可以从一个表中获取所需的结果:

select e.* from employee e where e.department_id in (1,2,3)

** 更新**

我的问题是 - 作为 JPA Criteria 的新功能,并且来自已弃用的 Hibernate Criteria - 我使用了 CriteriaBuilder 中的所有 API,例如 (equal, notEqual, isNull, like, .....);因此,使用了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);
}

但是,当然,第一个解决方案更容易。

【问题讨论】:

    标签: java hibernate jpa jpa-2.2


    【解决方案1】:

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

    可以,但我认为最好用Join来做。

    最后,即使表有信息,你的 JPA 实体,也就是 Criteria-api 理解的,没有那个 ID,它有一个对具有那个 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 个没有连接的查询。

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

    【讨论】:

    • 我完全不同意你的观点。它在 SQL 和 JPA 中是合乎逻辑的。另外,部门的负载与问题无关,如果我将其设置为FetchType.LAZY,则最初不会加载。 JPA 有 ID(请看下一个答案)。
    • 正如我之前告诉你的,如果你这样做 .get ().get () 将通过一个 in 子句触发查询的数量(当它应该只有一个时),如果你不这样做' t 放置像 Lazy 这样的关系会使内存过载。全部用于定义一个 Join。
    • 关于 JPA 有 ID 的事实,我想澄清一下,实体有一个参数指向另一个具有 ID 的实体,它不像 sql 查询中的表本身有ID,这就是为什么会产生1+N个查询,主查询和in子句的每个元素一个通过ID进行比较
    • 我已经尝试过 .get("department").get("id)。它可以作为一个没有连接或子查询的查询按预期工作。
    • 你能告诉我你的hibernate和JPA版本来测试它吗?在 5.2.1.Final 和 JPA2.1 版本中,它会为 in 子句的每个元素触发一个查询。
    【解决方案2】:
    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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-29
      • 2013-01-03
      • 1970-01-01
      • 2021-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多