Общее количество строк для разбивки на страницы с использованием API критериев JPA - PullRequest
23 голосов
/ 18 марта 2011

Я реализую функциональность типа «Расширенный поиск» для сущности в моей системе, так что пользователь может искать эту сущность, используя несколько условий (например, ne, gt, lt и т. Д.) Для атрибутов этой сущности.Я использую API критериев JPA для динамического создания запроса Criteria, а затем использую setFirstResult() & setMaxResults() для поддержки нумерации страниц.До этого момента все было в порядке, но теперь я хочу показать общее количество результатов в таблице результатов, но я не вижу прямого способа получить общее количество запросов Criteria.
Вот так выглядит мой код:

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

Мой набор результатов может быть большим, поэтому я не хочу загружать свои сущности для запроса на подсчет, поэтому скажите мне эффективный способ получить общее количество, например, метод rowCount() для критерия (я думаю, что он есть в Критериях Hibernate).

Ответы [ 4 ]

28 голосов
/ 18 марта 2011

Спасибо, Владимир! Я взял вашу идею и использовал отдельный запрос количества, чтобы использовать мой существующий массив предикатов. Окончательная реализация выглядит так:

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

Хотя это работает нормально, но все же я не уверен, почему я должен написать

em.createQuery(cq);

чтобы все заработало. Любая идея?

14 голосов
/ 18 марта 2011

Почему бы вам просто не использовать счет?

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();
2 голосов
/ 18 марта 2011

Если вы используете Hibernate в качестве JPA-провайдера, обратите внимание на проекции , особенно Projections.rowCount().

Возможно, вам придется выполнить запрос дважды, сначала получитеcount, то получите результаты.

Обратите внимание, что для простого JPA вам может понадобиться другой подход.

0 голосов
/ 20 июня 2019

Я думаю, оба ответа работают. Но ни один из них не является оптимальным. Проблема с ThinkFloyd заключается в том, что createQuery используется два раза. И Vladimir Ivanov создал два экземпляра CriteriaQuery, которые я считаю ненужными.

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)

Делай, иди работает. И это сделано в Kotlin . Вы делаете то же самое в Java .

...