Subquery in select clause with JPA Criteria API

lelmarir picture lelmarir · Jan 11, 2011 · Viewed 31k times · Source

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?

Answer

daca11 picture daca11 · Sep 15, 2016

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