J'implémente une fonctionnalité de type "Recherche avancée" pour une entité dans mon système de telle sorte que l'utilisateur puisse rechercher cette entité en utilisant plusieurs conditions (eq, ne, gt, lt, etc.) sur les attributs de cette entité. J'utilise l'API Criteria de JPA pour générer dynamiquement la requête Criteria, puis j'utilise setFirstResult()
& setMaxResults()
pour prendre en charge la pagination. Tout allait bien jusqu'à présent, mais maintenant je veux afficher le nombre total de résultats sur la grille de résultats, mais je n'ai pas vu de méthode simple pour obtenir le nombre total de requêtes de critères.
Voici à quoi ressemble mon code:
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();
Mon jeu de résultats pourrait être gros, donc je ne veux pas charger mes entités pour la requête de comptage, alors dites-moi un moyen efficace d'obtenir le nombre total comme la méthode rowCount()
sur les critères (je pense que c'est là dans les critères d'Hibernate).
Merci Vladimir! J'ai pris votre idée et utilisé une requête de comptage distincte pour utiliser mon tableau existant de prédicats. L'implémentation finale ressemble à ceci:
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()
Bien que cela fonctionne bien, mais je ne sais toujours pas pourquoi je dois écrire
em.createQuery(cq);
pour le faire fonctionner. Une idée?
Pourquoi n'utilisez-vous pas simplement le comptage?
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();
Si vous utilisez Hibernate comme fournisseur JPA, jetez un œil à projections , en particulier Projections.rowCount()
.
Cependant, vous devrez peut-être exécuter la requête deux fois, obtenez d'abord le nombre, puis les résultats.
Notez que pour JPA ordinaire, vous pourriez avoir besoin d'une autre approche.
Je suppose que les deux réponses fonctionnent. Mais aucun d'entre eux n'est optimal. Le problème avec ThinkFloyd
est que createQuery
est utilisé deux fois. Et Vladimir Ivanov
a créé deux instances de CriteriaQuery
qui, à mon avis, ne sont pas nécessaires.
val cb = entityManager.criteriaBuilder
val cq = cb.createQuery(ManualQuery::class.Java)
val manualQuery = cq.from(ManualQuery::class.Java)
val predicates = ArrayList<Predicate>()
/*
predications.....
*/
cq.select(manualQuery)
.where(*predicates.toTypedArray())
.orderBy(cb.desc(manualQuery.get<ZonedDateTime>("createdDate")))
val query = entityManager.createQuery(cq)
// total rows count
val count = query.resultList.size
val indexedQuery = query
.setFirstResult((currentPage - 1) * pageSize)
.setMaxResults(itemsPerPage)
Faire ça marche. Et cela se fait en Kotlin. Vous le faites de la même manière dans Java.