Нужна помощь в создании запроса критериев JPA

Я создаю свое первое веб-приложение Java EE с помощью Glassfish и JSF. Я новичок в критерии запроса, и у меня есть запрос, который мне нужно выполнить, но учебник javaee6 кажется немного тонким на примерах. Во всяком случае, мне сложно создавать запрос.

Цель: Я хочу вытащить компанию с сохраненными документами. У компаний есть отношения OneToMany с документами. Документы имеют отношения ManyToOne с несколькими таблицами, столбец "usertype" различает их.

Запрос MySQL:

SELECT USERID, COUNT(USERID) AS CNT 
FROM DOCUMENTS 
WHERE USERTYPE="COMPANY" 
GROUP BY USERID 
ORDER BY CNT DESC

Спасибо

- update-- Основываясь на отзывах пользователей, вот что я до сих пор:

        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Documents> cqry = cb.createQuery(Documents.class);
        //Intersting Stuff
        Root<Documents> root = cqry.from(Documents.class);
        Expression userid = root.get("userID");
        Expression usertype = root.get("userType");
        Expression count = cb.count(userid);
        cqry.multiselect(userid, count);
        Predicate userType = cb.equal(usertype, "COMPANY");
        cqry.where(userType);
        cqry.groupBy(userid);
        cqry.orderBy(cb.desc(count));
        //more boilerplate
        Query qry = em.createQuery(cqry);
        List<Documents> results = qry.getResultList();

Ошибка, которую я получаю:

Exception Description: Partial object queries are not allowed to maintain the cache or be edited.  You must use dontMaintainCache().

Типичная ошибка, для меня ничего не значит!

Ответ 1

Ваш запрос не возвращает полный объект сущности, поскольку вы выбираете только два поля данной таблицы (вот почему вы получаете сообщение об ошибке yadayadapartialyadayada).

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

Вместо простого CriteriaQuery<...> вам нужно создать кортеж CriteriaQuery<..>, вызвав CriteriaBuilder.createTupleQuery(). (В принципе, вы можете вызвать CriteriaBuilder.createQuery(...) и передать Tuple.class в качестве аргумента. Tuple - это своего рода класс подстановочных сущностей.)

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq= cb.createTupleQuery();

Root<Documents> root = cq.from(Documents.class);
Expression<Integer> userId = root.get("USERID");
Expression<String> userType = root.get("USERTYPE");
Expression<Long> count = cb.count(userId);

cq.multiselect(userId.alias("USERID"), count.alias("CNT"));
cq.where(cb.equal(userType, "COMPANY");
cq.groupBy(userId);
cq.orderBy(cb.desc(count));

TypedQuery<Tuple> tq = em.createQuery(cq);
for (Tuple t : tq.getResultsList()) {
  System.out.println(t.get("USERID"));
  System.out.println(t.get("CNT"));
}

(Доступ к полям Tuple дал мне ошибку, если я не использовал для них псевдонимы (в multiselect(...)). Вот почему я использовал псевдонимы, но это можно решить более чисто с использованием API JPA 2 Metamodel API, который подробно описан в спецификации.)

В документации для CriteriaQuery.multiselect(...) более подробно описывается поведение запросов с использованием объектов Tuple.

Ответ 2

Если вы используете Hibernate, это должно работать:

ProjectionList pl = Projections.projectionList()
.add(Projections.groupProperty("userid"))
.add(Projections.property("userid"))
.add(Projections.count("userid"));

Criteria criteria = session.createCriteria(Document.class)
.add(Restrictions.eq("usertype",usertype))
.setProjection(pl)
.addOrder(Order.desc("cnt"));

Надеюсь, что это поможет!