JPA 标准 API 不在代码简化和优化中

JPA Criteria API NOT IN Code Simplification and Optimization

我是 JPA 标准的新手 API。

2 个问题:

  1. 如何进一步简化此方法,第 3 条和第 4 条语句都需要 class 参数?
  2. 如何将 NOT IN 操作转换为 NOT EXISTS 以提高性能?

    public List<Pilot> getPilotsExcept(final Integer... entityIds) {
    
      final CriteriaBuilder critBuilder = em.getCriteriaBuilder();
    
      final CriteriaQuery<Pilot> critQuery =
            critBuilder.createQuery(getEntityClass());
    
      final Root<Pilot> root = critQuery.from(getEntityClass());  // Why pass the class again?
    critQuery.select(root).where(
        critBuilder.not(root.get("id").in(Arrays.asList(entityIds))));
    
      final TypedQuery<Pilot> typedQuery = em.createQuery(critQuery);
      return typedQuery.getResultList();
    }
    

Why pass the class again?

在 Criteria API 中,查询组件由对象表示。所以 select 子句和 from 子句由对象表示:

  • select子句表示如下:

    CriteriaQuery<Pilot> critQuery = critBuilder.createQuery(getEntityClass());
    

    上一行的Pilotclass表示查询结果的return类型

  • 另一方面,查询的 from 子句定义如下:

    final Root<Pilot> root = critQuery.from(getEntityClass());
    

在此示例中,RootCriteriaQuery 恰好具有相同的类型。但并非所有查询都是如此。在某些查询中,您可能希望 return 查询中的一个或多个属性(但不是查询中涉及的实体的所有属性)。在这些情况下,您在 CriteriaQuery 中指定的类型将不同于您在 Root 中使用的类型。例如,假设您的 Pilot 实体中有一个 name 属性,并且您只对 Pilot 实体的名称列表感兴趣。在这种情况下,您的 CriteriaQuery 将定义如下:

    CrtieriaQuery<String> cq = cb.createQuery(String.class);

但在您的示例中,Root 的定义没有改变。

NOT EXISTS expression

为了能够将您的 not in 表达式转换为 not exists 形式,您必须使用 ID 制定子查询。以下是 JPA 规范的摘录:

An EXISTS expression is a predicate that is true only if the result of the subquery consists of one ormore values and that is false otherwise. The syntax of an exists expression is

exists_expression::= [NOT] EXISTS (subquery) Of course, this must be translated to the Criteria API, in which case the syntax will be

not(exists(subquery)).

下面是一个创建子查询的例子(伪代码):

Subquery<Integer> sq = critQuery.subquery(Integer.class);
Root<Pilot> pilot = sq.from(Pilot.class);
sq.select(pilot.<Integer>get("id"));
sq.where(<some_condition>);