使用 CriteriaBuilder 从子查询中的路径中选择?

Selecting from path in subquery with CriteriaBuilder?

CriteriaBuilder 如何用于构造 SELECT a FROM e.attributes a .... 形式的 子查询,其中 e 是外部查询中引用的某个实体?

我有一些实体 类 涉及自由形式的键值结构(它有自己的问题,但这是我所拥有的)。我需要找到存在某些键值对的实体。我可以将其编写为以下形式的 JPQL 查询:

SELECT e FROM Entity e 
WHERE e.type = 'foo' 
AND EXISTS (SELECT a FROM e.attributes a 
             WHERE a.key = 'bar' 
               AND a.value = 'baz')

对于固定的查询字符串,我可以使用 EntityManager.createQuery():

创建查询
EntityManager em = /* ... */;
TypedQuery<Entity> tq = em.createQuery(queryString, Entity.class);

实际上,查询中有多个EXISTS,所以我需要使用CriteriaBuilder 构造查询。到目前为止我得到的最接近的是子查询SELECT a from Attributes a WHERE ...,但不限于e.attributes,当然:

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Entity> query = cb.createQuery(Entity.class);
Root<Entity> root = query.from(Entity.class);

Subquery<Attribute> subquery = query.subquery(Attribute.class);
Root<Attribute> subroot = subquery.from(Attribute.class); // too broad
subquery.select(subroot)
    .where(cb.and(//
        cb.equal(subroot.get("key"), cb.literal("bar")),
        cb.equal(subroot.get("value"), cb.literal("baz"))));

query.select(root)
    .where(cb.and(//
        cb.equal(root.get("type"), cb.literal("foo")), //
        cb.exists(subquery)));

子查询上有许多 correlate() 方法,我想知道我是否需要在外部查询中加入实体及其属性,然后以某种方式进行 correlate(),但我不确定EE7 javadocs correlation 到底做了什么(但是 correlate() 做 return From,这意味着我可以 SELECT 从它,这是有前途的)。

我最终在 JPA 2.0 spec 中找到了这个问题的答案。相关查询的第 276 页上有一个示例:

Example 4: A Special case

In order to express some correlated subqueries involving unidirectional relationships, it may be useful to correlate the domain of the subquery with the domain of the containing query. This is performed by using the correlate method of the Subquery interface.

For example:

CriteriaQuery<Customer> q = cb.createQuery(Customer.class);
Root<Customer> customer = q.from(Customer.class);
Subquery<Long> sq = q.subquery(Long.class);
Root<Customer> customerSub = sq.correlate(customer);
Join<Customer,Order> order = customerSub.join(Customer_.orders);
q.where(cb.gt(sq.select(cb.count(order)), 10))
.select(customer);

This query corresponds to the following Java Persistence query language query:

SELECT c
FROM Customer c
WHERE (SELECT COUNT(o) FROM c.orders o) > 10

FROM 子句的语法(同一规范中的第 173 页)给出的语法是:

from_clause ::=  FROM  identification_variable_declaration  ...
identification_variable_declaration ::= range_variable_declaration ...
range_variable_declaration ::= entity_name [AS] identification_variable

我不清楚 e.attributes 这样的东西怎么会是“entity_name”。事实证明,子查询 FROM 子句的语法中实际上还有一个完整的其他产品,其中包括集合支持:

subquery_from_clause ::= FROM subselect_identification_variable_declaration ...
subselect_identification_variable_declaration ::=
  identification_variable_declaration |
  derived_path_expression [AS] identification_variable {join}* |
  derived_collection_member_declaration
derived_path_expression ::=
  superquery_identification_variable.{single_valued_object_field.}*collection_valued_field |
  superquery_identification_variable.{single_valued_object_field.}*single_valued_object_field