Search code examples
jpaselectsubquerycriteria

Subquery in select clause with JPA Criteria API


I'm trying, as in title, to insert a subquery in select clause like in this simple SQL:

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

this is obviously only a mock query just to make my point. (The point would be to get the last invoice for each item returned by the query.)

I've tried this:

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

but I only get:

java.lang.IllegalStateException: Subquery cannot occur in select clause

How can I get a similar result?


Solution

  • It is supported in JPA 2.1 and Hibernate 5.0. You just had to add getSelection() to the subquery argument in the multiselect of the main query.

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

    Take a look at this working example:

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