I try to convert a sql query to Criteria API without success so far. I can create two separate queries which return the values I need, but I don't know how to combine them in a single query.
Here is the sql statement which works:
select company.*, ticketcount.counter from company
join
(select company, COUNT(*) as counter from ticket where state<16 group by company) ticketcount
on company.compid = ticketcount.company;
This Criteria query returns the inner query results:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<intCompany> qTicket = cb.createQuery(intCompany.class);
Root<Ticket> from = qTicket.from(Ticket.class);
Path groupBy = from.get("company");
Predicate state = cb.notEqual(from.<State>get("state"), getStateById(16));
qTicket.select(cb.construct(
intCompany.class, cb.count(from),from.<Company>get("company")))
.where(state).groupBy(groupBy);
em.createQuery(qTicket).getResultList();
In the application I defined a small wrapper/helper class:
public class intCompany{
public Company comp;
public Long opentickets;
public intCompany(Long opentickets,Company comp){
this.comp = comp;
this.opentickets = opentickets;
}
public intCompany(){
}
}
So does anyone has an idea how to get this working?
Update
Thank you. I changed my criteria query as you suggested. I just had to add a loop at the end to get the information I wanted.
List<intCompany> result = em.createQuery(cq).getResultList();
List<Company> cresult = new ArrayList();
for(intCompany ic: result){
ic.comp.setOpentickets(ic.opentickets.intValue());
cresult.add(ic.comp);
}
return cresult;
Maybe it is just not possible to convert the original sql to Criteria API.
Another update
I figured out I had to change the original sql expression to
select company.*, ticketcount.counter from company
left join
(select company, COUNT(*) as counter from ticket where state<16 group by company) ticketcount
on company.compid = ticketcount.company;
Otherwise I do not get companies with no entries in the ticket table.
So are there any other suggestions?
I had similar problem. My solution was to use left outer joins.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Entity> query = cb.createQuery(Entity.class);
Root<Entity> root = query.from(Entity.class);
Join<Entity,ChildEntity> join = root.join(Entity_.children, JoinType.LEFT);
query.groupBy(root.get( Entity_.id ));
query.select(
cb.construct(
EntityDTO.class,
root.get( Entity_.id ),
root.get( Entity_.name ),
cb.count(join)
));
This JoinType.LEFT
guarantees that you will get Entity records (companies) even if it doesn't have any child entities (tickets).
Entity class:
@Entity
public class Entity {
...
@OneToMany(targetEntity = ChildEntity.class, mappedBy = "parent", fetch = FetchType.LAZY, orphanRemoval = false)
private Set<ChildEntity> objects;
...
}
Static model:
@StaticMetamodel( Entity.class )
public class Entity_ {
public static volatile SingularAttribute<Entity, Long> id;
public static volatile SingularAttribute<Entity, String> name;
...
public static volatile SetAttribute<Entity, ChildEntity> objects;
}
You have almost everything done.
//---//
CriteriaBuilder cb = em.getCriteriaBuilder();
//Your Wrapper class constructor must match with multiselect arguments
CriteriaQuery<IntCompany> cq = cb.createQuery(IntCompany.class);
//Main table
final Root<Ticket> fromTicket= cq.from(Ticket.class);
//Join defined in Ticket Entity
final Path company = fromTicket.get("company");
//Data to select
cq.multiselect(cb.count(from), company);
//Grouping
cq.groupBy(company);
//Restrictions (I don't really understand what you're querying)
Predicate p = cb.lessThan(fromTicket.get("state"), 16);
//You can add more restrictions
// p = cb.and/or(p, ...);
cq.where(p);
List<IntCompany> results = entityManager.createQuery(cq).getResultList();
This should work as expected.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.