Общее количество строк для разбивки на страницы с использованием API-критериев JPA
Я реализую функцию "Расширенный поиск" для Entity в моей системе, так что пользователь может искать этот объект, используя несколько условий (eq, ne, gt, lt, и т.д.) для атрибутов этого объекта. Я использую API критериев JPA для динамического генерации запроса Criteria, а затем с помощью setFirstResult()
и setMaxResults()
для поддержки разбивки на страницы. Все было хорошо до этого момента, но теперь я хочу показать общее количество результатов по сетке результатов, но я не видел прямого способа получить общий счет запроса критериев.
Вот как выглядит мой код:
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();
Мой результирующий набор может быть большим, поэтому я не хочу загружать свои объекты для запроса count, поэтому скажите мне эффективный способ получить общий счет, например, метод rowCount()
по критериям (я думаю, что он существует в Hibernate Criteria).
Ответы
Ответ 1
Спасибо Владимиру!
Я взял вашу идею и использовал отдельный запрос подсчета, чтобы использовать мой существующий массив предикатов в нем. Окончательная реализация выглядит следующим образом:
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);
чтобы заставить его работать. Любая идея?
Ответ 2
Почему бы вам просто не использовать счет?
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();
Ответ 3
Если вы используете Hibernate, так как ваш JPA-Provider просматривает прогнозы, особенно Projections.rowCount()
.
Возможно, вам придется выполнить запрос дважды, но сначала получите счетчик, затем получите результаты.
Обратите внимание, что для простой JPA вам может понадобиться другой подход.
Ответ 4
У меня такие же проблемы в моих приложениях, но это решение не работает, когда вы используете соединение. спасибо заранее за ваш совет
Ответ 5
Я думаю, оба ответа работают. Но ни один из них не является оптимальным. Проблема с 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)
Делать идти, это работает. И это сделано в Котлине. Вы делаете то же самое в Java.