2011-03-18 19 views
22

Estoy implementando el tipo de funcionalidad de "búsqueda avanzada" para una entidad en mi sistema de forma que el usuario pueda buscar esa entidad usando múltiples condiciones (eq, ne, gt, lt, etc.) en los atributos de esta entidad. Estoy utilizando la API Criteria de JPA para generar dinámicamente la consulta Criteria y luego usando setFirstResult() & setMaxResults() para admitir la paginación. Todo estaba bien hasta este punto, pero ahora quiero mostrar el número total de resultados en la grilla de resultados, pero no veo una manera directa de obtener el conteo total de la consulta de Criteria.
Así es como mi código es el siguiente:Recuento total de filas para paginación usando criterios JPA API

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<Brand> cQuery = builder.createQuery(Brand.class); 
Root<Brand> from = cQuery.from(Brand.class); 
CriteriaQuery<Brand> select = cQuery.select(from); 
. 
. 
//Created many predicates and added to **Predicate[] pArray** 
. 
. 
select.where(pArray); 
// Added orderBy clause 
TypedQuery typedQuery = em.createQuery(select); 
typedQuery.setFirstResult(startIndex); 
typedQuery.setMaxResults(pageSize); 
List resultList = typedQuery.getResultList(); 

Mi conjunto de resultados podría ser grande por lo que no quiero cargar mis entidades para la consulta de recuento, así que dime manera eficaz de obtener el recuento total como método de rowCount() Criteria (creo que está ahí en los Criterios de Hibernate).

Respuesta

24

Gracias Vladimir! Tomé su idea y utilicé una consulta de recuento separada para usar mi matriz existente de predicados. La implementación final es el siguiente:

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<Brand> cQuery = builder.createQuery(Brand.class); 
Root<Brand> from = cQuery.from(Brand.class); 
CriteriaQuery<Brand> select = cQuery.select(from); 
. 
. 
//Created many predicates and added to **Predicate[] pArray** 
. 
. 
CriteriaQuery<Long> cq = builder.createQuery(Long.class); 
cq.select(builder.count(cq.from(Brand.class))); 
// Following line if commented causes [org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.enabled' [select count(generatedAlias0) from xxx.yyy.zzz.Brand as generatedAlias0 where (generatedAlias1.enabled=:param0) and (lower(generatedAlias1.description) like :param1)]] 
em.createQuery(cq); 
cq.where(pArray); 
Long count = em.createQuery(cq).getSingleResult(); 
. 
. 
select.where(pArray); 
. 
. 
// Added orderBy clause 
TypedQuery typedQuery = em.createQuery(select); 
typedQuery.setFirstResult(startIndex); 
typedQuery.setMaxResults(pageSize); 
List resultList = typedQuery.getResultList() 

Aunque esto está funcionando bien, pero todavía no estoy seguro de por qué tengo que escribir

em.createQuery(cq); 

para que funcione. ¿Alguna idea?

+0

¿Pudo averiguar por qué exactamente necesita agregar 'em.createQuery (cq);'? Gracias – Ittai

+0

Oye, esto es justo lo que necesitaba ... ¡pero también me gustaría saber por qué es necesaria esta línea! –

+0

Sospecho que tiene que ver con el hecho de que los 'Predicados' se están construyendo usando' Root <> 'de esta parte' Root from = cQuery.from (Brand.class); 'y luego se usan para ambos la consulta de recuento y la consulta paginada. AFAIK necesita construir los predicados dos veces usando ambos objetos 'Root <>'. El segundo está oculto en esta línea 'cq.select (builder.count (cq.from (Brand.class)));'. Por qué exactamente 'em.createQuery (cq);' funciona, no sé, IMO debería arrojar una excepción. – Neilos

2

Si está utilizando Hibernate como su proveedor de JPA eche un vistazo a projections, especialmente Projections.rowCount().

Es posible que deba ejecutar la consulta dos veces, primero obtenga el recuento y luego obtenga los resultados.

Tenga en cuenta que para JPA simple, puede necesitar algún otro enfoque.

+3

-1. El OP indicó claramente que está buscando respuestas con respecto a la "API de criterios de JPA", ¿no cree que su respuesta es algo engañosa? (Implicar las proyecciones de hibernación es la forma de hacerlo). Lo digo porque cuando tropecé con la pregunta, vi tu respuesta primero y me confundió pensar que ese era el camino (especialmente porque había un agregado entre tú y la siguiente respuesta que te convertía en la única respuesta visible). – Ittai

+0

@Ittai bueno, él menciona Hibernate e incluso marcó la pregunta en consecuencia ... – Thomas

+0

Creo que cuando lees la pregunta ves que no se trata de hibernar, es solo que muchas veces en SO ves que la gente responde lo que piense que el OP necesita y no lo que el OP solicita. Como parece un error honesto si editas tu pregunta, cambiaré mi voto. – Ittai

14

¿Por qué no solo usa conteo?

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery<Long> cQuery = builder.createQuery(Long.class); 
Root<Brand> from = cQuery.from(Brand.class); 
CriteriaQuery<Long> select = cQuery.select(builder.count(from)); 
. 
. 
//Created many predicates and added to **Predicate[] pArray** 
. 
. 
select.where(pArray); 
// Added orderBy clause 
TypedQuery<Long> typedQuery = em.createQuery(select); 
typedQuery.setFirstResult(startIndex); 
//typedQuery.setMaxResults(pageSize); 
// here is the size of your query 
Long result = typedQuery.getSingleResult(); 
Cuestiones relacionadas