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

时间:2018-08-01 12:09:26

标签: java jpa jpql criteria-api

如何使用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()方法,我想知道是否需要在外部查询中将具有其属性的Entity联接起来,然后以某种方式关联(),但是我不确定从EE7 javadocs确切的相关性是做什么的(但是correlate()确实返回一个From,这意味着我可以从中进行SELECT,这很有希望)。

1 个答案:

答案 0 :(得分:0)

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

  

示例4:特例

     

为了表达一些涉及单向关系的相关子查询,将子查询的域与包含查询的域相关联可能很有用。这是通过使用Subquery接口的相关方法来完成的。

     

例如:

   // Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        flatDir {
            dirs 'libs'
        }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
     

此查询对应于以下Java Persistence查询语言查询:

     
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);

令我困扰了一会儿,让我想到也许我的JPQL可能不合法,是因为FROM子句的语法(同一规范中的第173页)的给出方式为:

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

我不清楚from_clause ::= FROM identification_variable_declaration ... identification_variable_declaration ::= range_variable_declaration ... range_variable_declaration ::= entity_name [AS] identification_variable 之类的东西如何可以成为“ entity_name”。事实证明,子查询FROM子句的语法中实际上还有其他整个产品,其中包括集合支持:

e.attributes