Estoy tratando de implementar los siguientes métodos convenientes:

/**
 * Counts the number of results of a search.
 * @param criteria The criteria for the query.
 * @return The number of results of the query.
 */
public int findCountByCriteria(CriteriaQuery<?> criteria);

En modo de Hibernación, esto se hace por

criteria.setProjection(Projections.rowCount());

Lo que es el equivalente a la anterior en JPA? He encontrado numerosos simple recuento de ejemplos, pero ninguno de ellos hizo uso de un CriteriaQuery cuyo recuento de filas debe ser determinado.

EDICIÓN:

Por desgracia he descubierto que @Pascal la respuesta no es la correcta. El problema es muy sutil y sólo se muestra cuando se utiliza combinaciones:

//Same query, but readable:
//SELECT *
//FROM Brain b
//WHERE b.iq = 170

CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Join<Object, Object> brainJoin = root.join("brain");
Predicate iqPredicate = cb.equal(brainJoin.<Integer>get("iq"), 170);
query.select(root).where(iqPredicate);

Cuando se llama a findCountByCriteria(query), muere con la siguiente excepción:

org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.iq' [select count(generatedAlias0) from xxx.tests.person.dom.Person as generatedAlias0 where generatedAlias1.iq=170]

Es hay alguna otra manera de proporcionar una CountByCriteria método?

  • Donde usted es capaz de encontrar una solución? Estoy en el mismo cruce y no está seguro de cómo proceder. Gracias.
  • Estoy bastante seguro de que no es posible, después de todo.
  • Al parecer, es posible después de todo. Por favor, ver Jose Luis Martin de la respuesta. Yo no probarlo, pero parece que la solución verdadera para mí.
  • Muchas gracias por el aviso, va a tener una mirada en ella cuando vuelvo a hacerlo.
InformationsquelleAutor blubb | 2010-10-22

7 Comentarios

  1. 17

    Escribí una clase de utilidad, JDAL JpaUtils hacerlo:

    • los resultados del conteo: Long count = JpaUtils.count(em, criteriaQuery);
    • copia CriteriaQueries: JpaUtils.copyCriteria(em, criteriaQueryFrom, criteriaQueryTo);
    • obtener el número de criterios: CriteriaQuery<Long> countCriteria = JpaUtils.countCriteria(em, criteria)

    y así sucesivamente…

    Si usted está interesado en el código fuente, ver JpaUtils.java

    • Se debe mencionar que el autor de dicha clase de utilidad. Aparte de esto, esta respuesta se merece la máxima puntuación, y me otorgará la recompensa a esta respuesta tan pronto como el período obligatorio de espera de 24 h más. Gracias!
    • Gracias, acabo de editar. No está totalmente probado pero parece que funciona con criterios complejos
    • Este conjunto de herramientas se basa en la Primavera, que el autor de la pregunta nunca dijo que él tiene…
    • usted puede ayudar sobre cómo alcanzar la siguiente cuenta de la consulta usando JPA criterios de la API? SELECT COUNT(DISTINCT col1, col2, col3) FROM my_table;
  2. 4

    He ordenado a cabo con el cb.createQuery() (sin el resultado de parámetro de tipo):

    public class Blah() {
    
        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
        CriteriaQuery query = criteriaBuilder.createQuery();
        Root<Entity> root;
        Predicate whereClause;
        EntityManager entityManager;
        Class<Entity> domainClass;
    
        ... Methods to create where clause ...
    
        public Blah(EntityManager entityManager, Class<Entity> domainClass) {
            this.entityManager = entityManager;
            this.domainClass = domainClass;
            criteriaBuilder = entityManager.getCriteriaBuilder();
            query = criteriaBuilder.createQuery();
            whereClause = criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
            root = query.from(domainClass);
        }
    
        public CriteriaQuery<Entity> getQuery() {
            query.select(root);
            query.where(whereClause);
            return query;
        }
    
        public CriteriaQuery<Long> getQueryForCount() {
            query.select(criteriaBuilder.count(root));
            query.where(whereClause);
            return query;
        }
    
        public List<Entity> list() {
            TypedQuery<Entity> q = this.entityManager.createQuery(this.getQuery());
            return q.getResultList();
        }
    
        public Long count() {
            TypedQuery<Long> q = this.entityManager.createQuery(this.getQueryForCount());
            return q.getSingleResult();
        }
    }

    Espero que ayude 🙂

    Lo que hice es algo así como un generador de un CriteriaBuilder donde se puede construir una consulta y de la lista de llamadas() o count() con los mismos criterios restricciones

  3. 1

    Estás buscando algo como esto?

    /**
     * Counts the number of results of a search.
     * 
     * @param criteria The criteria for the query.
     * @return The number of results of the query.
     */
    public <T> Long findCountByCriteria(CriteriaQuery<?> criteria) {
        CriteriaBuilder builder = em.getCriteriaBuilder();
    
        CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
        Root<?> entityRoot = countCriteria.from(criteria.getResultType());
        countCriteria.select(builder.count(entityRoot));
        countCriteria.where(criteria.getRestriction());
    
        return em.createQuery(countCriteria).getSingleResult();
    }

    Que usted podría utilizar como esto:

    //a search based on the Criteria API
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
    Root<Person> personRoot = criteria.from(Person.class);
    criteria.select(personRoot);
    Predicate personRestriction = builder.and(
        builder.equal(personRoot.get(Person_.gender), Gender.MALE),
        builder.equal(personRoot.get(Person_.relationshipStatus), RelationshipStatus.SINGLE)
    );
    criteria.where(personRestriction);
    //...
    
    //and to get the result count of the above query
    Long count = findCountByCriteria(criteria);

    PS: no sé si esta es la mejor/mejor manera de implementar esto, todavía en el aprendizaje de la API de Criterios…

    • Me alegro ayudó. Siéntase libre de aceptar la respuesta, a continuación, (la marca verde debajo del contador de votos de la izquierda).
    • Desafortunadamente, esta solución sólo funciona trivial de las consultas. (Ver la edición en la pregunta para una explicación, es demasiado largo para un comentario.)
  4. 1

    Que la idea de los criterios de las consultas es que son fuertemente tipados. Para cada solución, donde estés usando raw tipos (sin medicamentos genéricos en CriteriaQuery o Raíz o Root), estas soluciones están en contra de que la idea principal. Acabo de ejecutar en el mismo problema y yo estoy luchando para resolverlo en un «buen» (junto con JPA2) en el camino.

  5. 1

    Ninguna de las soluciones anteriores funciona para EclipseLink 2.4.1, que todo terminó con un recuento de un producto Cartesiano (N^2), aquí es un pequeño hack para EclipseLink, el único inconveniente es que no sé qué va a pasar si se selecciona más de una Entidad, se intentará contar desde el 1 de encontrar la Raíz de su CriteriaQuery, esta solución NO funciona para Hibernar aunque (JDAL, pero JDAL no funciona para EclipseLink)

    public static Long count(final EntityManager em, final CriteriaQuery<?> criteria)
      {
        final CriteriaBuilder builder=em.getCriteriaBuilder();
        final CriteriaQuery<Long> countCriteria=builder.createQuery(Long.class);
        countCriteria.select(builder.count(criteria.getRoots().iterator().next()));
        final Predicate
                groupRestriction=criteria.getGroupRestriction(),
                fromRestriction=criteria.getRestriction();
        if(groupRestriction != null){
          countCriteria.having(groupRestriction);
        }
        if(fromRestriction != null){
          countCriteria.where(fromRestriction);
        }
        countCriteria.groupBy(criteria.getGroupList());
        countCriteria.distinct(criteria.isDistinct());
        return em.createQuery(countCriteria).getSingleResult();
      }
    • ¡Uy!, EclipseLink añade raíces de predicado a los criterios. Acabo de trabajo en torno a la versión 2.0. Gracias!
  6. 1

    si quieres que el resultado y el recuento de todos los elementos como la Primavera de los Datos Página-Elemento, se pueden hacer dos consultas. Lo que puedes hacer es separar a los criterios de la consulta de ejecución.

    Ejemplo, para buscar Usuarios por Ciudad

     public List<User> getUsers(int userid, String city, other values ...) {
    
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<User> q = cb.createQuery(User.class);
        Root<User> c = q.from(User.class);
    
        List<Predicate> conditions = createConditions(c, cb, userid, city, ...other values);
        List<User> users = em.createQuery(q.select(c).where(conditions.toArray(new Predicate[] {})).distinct(true))
                .setMaxResults(PAGE_ELEMENTS).setFirstResult(page * PAGE_ELEMENTS).getResultList();
        return users;
    }

    addiional para el Método getUser usted puede construir una segunda que va a contar sus elementos

    public Long getElemCount(int userid,  String city, ...other values) {
    
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Long> q = cb.createQuery(Long.class);
        Root<Location> root = q.from(Location.class);
    
        List<Predicate> conditions = createConditions(root, cb, userid, page, city, filter, module, isActive);
        Long userCount = em.createQuery(q.select(cb.count(root)).where(conditions.toArray(new Predicate[] {})).distinct(true))
                .getSingleResult();
    
        return userCount;
    }

    y la createConditions Método de manejar, por lo que no es necesario duplicar la lógica de los criterios.

    <T> List<Predicate> createConditions(Root<T> root, CriteriaBuilder cb, int userid, String city, ... other values) {
    
        Join<User, SecondEntity> usr = root.join("someField");
        //add joins as you wish
    
        /*
         * Build Conditions
         */
        List<Predicate> conditions = new ArrayList<>();
    
        conditions.add(cb.equal(root.get("id"), userid));
    
        if (!city.equals("")) {
           conditions.add(cb.like(...));
        }
    
       //some more conditions...
    
        return conditions;
    }

    en su contoller, puedes hacer algo como

    largo elementCount = yourCriteriaClassInstance.getElementCount(…);
    Los usuarios de la lista = yourCriteriaClassInstance.getUsers(…)

  7. -3

    hago algo como thath con hibernate y criterios de api

    public Long getRowsCount(List<Criterion> restrictions ) {
           Criteria criteria = getSession().createCriteria(ThePersistenclass.class);
           for (Criterion x : restrictions)
                 criteria.add(x);
       return criteria.setProjection(Projections.rowCount()).uniqueResult();        

    }

    la esperanza de ayudar a

    • Sé que esto es posible con la API de Hibernate (como he dicho en la pregunta). Sólo estoy interesado en una alternativa el uso de la JPA de la interfaz.

Dejar respuesta

Please enter your comment!
Please enter your name here