2012-01-13 26 views
5

Intento convertir una consulta sql a API de Criteria sin éxito hasta el momento. Puedo crear dos consultas separadas que devuelvan los valores que necesito, pero no sé cómo combinarlos en una sola consulta.API de criterios JPA 2 +: definición de una subconsulta

Aquí está la instrucción SQL que trabaja:

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; 

Esta consulta Criterios devuelve los resultados de la consulta interna:

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

En la aplicación definí una pequeña clase de contenedor/ayudante:

public class intCompany{ 
    public Company comp; 
    public Long opentickets; 
    public intCompany(Long opentickets,Company comp){ 
     this.comp = comp; 
     this.opentickets = opentickets; 
    } 
    public intCompany(){ 

    } 
} 

Entonces, ¿alguien tiene una idea de cómo hacerlo funcionar?

actualización

Gracias. Cambié mi consulta de criterio como sugirió. Solo tuve que agregar un bucle al final para obtener la información que quería.

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; 

Tal vez simplemente no es posible convertir el sql original a API Criteria.

Otra actualización

me di cuenta de que tenía que cambiar la expresión SQL original al

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; 

De lo contrario no consigo empresas sin entradas en la tabla de entradas.

¿Hay alguna otra sugerencia?

Respuesta

2

Tienes casi todo hecho.

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

Esto debería funcionar como se esperaba.

2

Tuve un problema similar. Mi solución fue usar uniones externas izquierdas.

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

Este JoinType.LEFT garantiza que obtendrá registros de entidad (empresas), incluso si no tiene ningún entidades secundarias (billetes). clase

Entidad:

@Entity 
public class Entity { 
    ... 

    @OneToMany(targetEntity = ChildEntity.class, mappedBy = "parent", fetch = FetchType.LAZY, orphanRemoval = false) 
    private Set<ChildEntity> objects; 

    ... 
} 

modelo estático:

@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; 
} 
Cuestiones relacionadas