2010-10-22 4 views
11

Я пытаюсь реализовать следующий удобный метод: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:

Я, к сожалению, обнаружил, что @ ответ Паскаля не правильным. Проблема очень тонкая и показывает только при использовании присоединяется:

// 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 ?

+2

Где вы можете найти решение? Я нахожусь на том же узле и не знаю, как действовать дальше. Благодарю. – Ittai

+0

@Ittai: Я уверен, что это невозможно. – blubb

+0

@Ittai: Видимо, это возможно в конце концов. Пожалуйста, см. Ответ Хосе Луиса Мартина. Я не пробовал, но это похоже на истинное решение для меня. – blubb

ответ

17

Я написал вспомогательный класс, JDAL JpaUtils сделать это:

  • результаты подсчета: Long count = JpaUtils.count(em, criteriaQuery);
  • копия CriteriaQueries: JpaUtils.copyCriteria(em, criteriaQueryFrom, criteriaQueryTo);
  • GET критерии подсчета: CriteriaQuery<Long> countCriteria = JpaUtils.countCriteria(em, criteria)

и так далее ...

Если вы заинтересованы в исходном коде см JpaUtils.java

+0

Вы должны упомянуть, что вы являетесь автором указанного класса утилиты. Помимо этого, этот ответ заслуживает полных оценок, и я вручу награду за этот ответ, как только будет завершен период ожидания 24 часа. Благодаря! – blubb

+0

@blubb Спасибо, я просто редактирую. Не полностью протестирован, но, похоже, работает со сложными критериями. –

+0

Этот инструментарий опирается на весну, о которой никогда не говорил, что он ... – Amalgovinus

1

Вы ищете что-то в этом роде?

/** 
* 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: Я не знаю, если это правильный/лучший способ осуществить это, до сих пор изучение Criteria API ...

+0

@Simon: Рад, что это помогло. Не стесняйтесь принять ответ тогда (зеленый тик ниже счетчика голосов слева). –

+0

К сожалению, это решение работает только для тривиальных запросов. (См. Редактирование вопроса для объяснения, слишком длинный для комментария.) – blubb

-3

я действительно somethig как Тат с Hibernate и criteria 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();   

}

надежда помощь

+0

Я знаю, что это возможно с помощью Hibernate API (как я уже сказал в вопросе). Меня интересует только альтернатива, использующая интерфейс JPA. – blubb

3

Я отсортирован это с помощью ЦЕНТИБАР.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() с теми же критериями ограничений

1

Вся идея запросов критериев заключается в том, что они строго типизированы. Поэтому каждое решение, в котором вы используете необработанные типы (без генериков в CriteriaQuery или Root или Root), - эти решения противоречат этой основной идее. Я просто столкнулся с той же проблемой, и я изо всех сил пытаюсь ее решить в «правильном» (вместе с JPA2) способом.

1

Ни одно из вышеперечисленных решений не работает для EclipseLink 2.4.1, все они заканчиваются подсчетом на декартово произведение (N^2), вот небольшой взлом для EclipseLink, единственным недостатком является то, что я не знаю что произойдет, если вы выберете FROM более одного Entity, он попытается подсчитать с первого найденного 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(); 
    } 
+0

Упс !, EclipseLink добавляет корни из предиката в критерии. Я просто работаю над версией 2.0. Благодаря! –

1

Если вы хотите получить результат и количество всех элементов, таких как Spring Data, Page -Element, вы можете выполнить два запроса. Что вы можете сделать, так это отделить критерии от выполнения запроса.

Пример для поиска пользователей по городу

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 к методу 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; 
} 

в вашем вы можете регулятора, сделать что-то вроде

длинной ElementCount = yourCriteriaClassInstance.getElementCount (...); Список пользователей = yourCriteriaClassInstance.getUsers (...)