【问题标题】:Subquery in select clause with JPA Criteria API使用 JPA Criteria API 的 select 子句中的子查询
【发布时间】:2011-01-11 20:33:55
【问题描述】:

如标题所示,我正在尝试在 select 子句中插入一个子查询,就像在这个简单的 SQL 中一样:

SELECT id, name, (select count(*) from item) from item

这显然只是一个模拟查询,只是为了说明我的观点。 (重点是获取查询返回的每个项目的最后一张发票。)

我试过了:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> c = cb.createTupleQuery();
Root<Item> item= c.from(Item.class);

Subquery<Long> scount = c.subquery(Long.class);
Root<Item> sarticolo = scount.from(Item.class);
scount.select(cb.count(sitem));

c.multiselect(item.get("id"),item.get("nome"), scount);

Query q = em.createQuery(c);
q.setMaxResults(100);
List<Tuple> result = q.getResultList();

for(Tuple t: result){
  System.out.println(t.get(0) + ", " + t.get(1) + ", " + t.get(2));
}

但我只得到:

java.lang.IllegalStateException: select子句中不能出现子查询

我怎样才能得到类似的结果?

【问题讨论】:

    标签: jpa select subquery criteria


    【解决方案1】:

    它在 JPA 2.1 和 Hibernate 5.0 中受支持。您只需将getSelection() 添加到主查询的multiselect 的子查询参数中。

    c.multiselect(item.get("id"),item.get("nome"), scount.getSelection());
    

    看看这个工作示例:

    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<NotificationInfo> cq = builder.createQuery(NotificationInfo.class); //wrapper class
    Root<Notification> n = cq.from(Notification.class); //root entity
    
    //Subquery
    Subquery<Long> sqSent = cq.subquery(Long.class);
    Root<NotificationUser> sqSentNU = sqSent.from(NotificationUser.class);
    sqSent.select(builder.count(sqSentNU));
    sqSent.where(
            builder.equal(sqSentNU.get(NotificationUser_.notification), n),  //join subquery with main query
            builder.isNotNull(sqSentNU.get(NotificationUser_.sendDate))
    );
    
    cq.select(
        builder.construct(
                NotificationInfo.class,
                n.get(Notification_.idNotification),
                n.get(Notification_.creationDate),
                n.get(Notification_.suspendedDate),
                n.get(Notification_.type),
                n.get(Notification_.title),
                n.get(Notification_.description),
                sqSent.getSelection()
        )
    );
    em.createQuery(cq).getResultList();
    

    【讨论】:

    • 我相信你的答案是正确的,但是你能写出 NotfiticationInfo 和 NotificationUser 的关系,以及它完成的 SQL 语句是什么吗?看起来不像:SELECT id, creationDate, suspendedDate, type, title, description, (select count(case when i don't follow) from notification) from notification
    【解决方案2】:

    JPA 不支持 select 子句中的子查询。

    您需要更改查询以便在 select 子句中不使用 require 子查询、执行多个查询或使用原生 SQL 查询。

    【讨论】:

      【解决方案3】:

      您需要合并您的子查询结果:

      Expression<ResultType> expression = criterioaBuilder.coalesce(subquery, criteriaBuilder.literal((ResultType) defaultResult);
      query.select(expression);
      

      【讨论】:

      • 这是一个非常有用的技巧。我没有在其他任何地方找到它。任何文档或教程中是否也提到了这一点?
      【解决方案4】:

      JPA 现在支持 select 子句中的子查询。

      编辑:
      JPA 2.1 JPQL BNF 支持 select 子句中的子查询,即使它不是必需的。据我所知,Eclipselink 也支持这个和 Hibernate(在 5.1 中测试)。

      【讨论】:

      • 您的答案中的“现在”一词究竟是什么版本的 JPA 规范?
      • "now" 指的是 JPA 的最新版本,即(是)2.1
      猜你喜欢
      • 2012-07-02
      • 2012-07-19
      • 2017-07-20
      • 2012-02-26
      • 2017-06-11
      • 2011-10-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多