JPA 2.0: подсчет для произвольного критерияQuery?
Я пытаюсь реализовать следующий удобный метод:
/**
* 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);
В Hibernate это делается с помощью
criteria.setProjection(Projections.rowCount());
Что эквивалентно приведенному выше в JPA? Я нашел множество простых примеров подсчета, но ни один из них не использовал CriteriaQuery, число строк которого должно быть определено.
EDIT:
Я, к сожалению, обнаружил, что ответ @Pascal неверен. Проблема очень тонкая и появляется только при использовании объединений:
// 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);
При вызове findCountByCriteria(query)
он умирает со следующим исключением:
org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.iq' [select count(generatedAlias0) from xxx.tests.person.dom.Person as generatedAlias0 where generatedAlias1.iq=170]
Есть ли другой способ предоставить такой метод CountByCriteria
?
Ответы
Ответ 1
Я написал класс утилиты JDAL JpaUtils:
- результаты подсчета:
Long count = JpaUtils.count(em, criteriaQuery);
- copy CriteriaQueries:
JpaUtils.copyCriteria(em, criteriaQueryFrom, criteriaQueryTo);
- получить критерии подсчета:
CriteriaQuery<Long> countCriteria = JpaUtils.countCriteria(em, criteria)
и т.д.
Если вас интересует исходный код, см. JpaUtils.java
Ответ 2
Я отсортировал это с помощью cb.createQuery() (без параметра типа результата):
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();
}
}
Надеюсь, это поможет:)
То, что я сделал, похоже на конструктор CriteriaBuilder, где вы можете построить запрос и список вызовов() или count() с теми же ограничениями критериев
Ответ 3
Вы ищете что-то вроде этого?
/**
* 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();
}
Что вы можете использовать следующим образом:
// 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: Я не знаю, является ли это правильным/лучшим способом реализовать это, все еще изучая API критериев...
Ответ 4
Вся идея запросов критериев состоит в том, что они строго типизированы. Поэтому каждое решение, в котором вы используете необработанные типы (без генериков в CriteriaQuery или Root или Root), - эти решения противоречат этой основной идее. Я просто столкнулся с одной и той же проблемой, и я изо всех сил пытаюсь ее решить в "правильном" (вместе с JPA2) способом.
Ответ 5
Ни одно из вышеперечисленных решений не работает для EclipseLink 2.4.1, все они заканчиваются подсчетом на декартово произведение (N ^ 2), вот небольшой взлом для EclipseLink, единственным недостатком является то, что я не знаю, что произойдет, если вы выберете FROM более одного Entity, он попытается подсчитать с 1-го найденного Root вашего CriteriaQuery, это решение НЕ работает для Hibernate, хотя (JDAL делает, но JDAL не работает для 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();
}
Ответ 6
если вы хотите получить результат и количество всех таких элементов, как Spring Data Page -Элемент, вы можете сделать два запроса. Что вы можете сделать, так это отделить критерии от выполнения запроса.
Пример поиска пользователей по городам
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;
}
дополняющий метод getUser, вы можете построить вторую, которая будет считать ваши элементы
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;
}
и метод createConditions будет обрабатывать оба, поэтому вам не нужно дублировать свою логику для критериев.
<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;
}
в вашем контоллере вы можете сделать что-то вроде
long elementCount = yourCriteriaClassInstance.getElementCount(...);
Список пользователей = yourCriteriaClassInstance.getUsers(...)
Ответ 7
Я делаю что-то вроде этого с hibernate и критерии 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();
}
помощь надежда