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

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

Цель: я хочу получить компанию с наибольшим количеством сохраненных документов. Компании имеют отношения OneToMany с документами. Documents имеет связь 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().

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


person Alan B. Dee    schedule 06.01.2012    source источник
comment
Попробуйте изменить тип запроса из CriteriaQuery‹Documents› в CriteriaQuery, чтобы выбрать количество, не нарушая API. Если вы также опубликуете дополнительную информацию о таблицах/сущностях, я могу попробовать другие способы.   -  person perissf    schedule 07.01.2012


Ответы (3)


Ваш запрос не возвращает объект сущности complete, так как вы выбираете только два поля данной таблицы (поэтому вы получаете сообщение об ошибке 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(...)). Вот почему я использовал псевдонимы, но это можно решить более аккуратно, используя JPA 2 API метамодели, который довольно подробно описан в спецификации.)

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

person Kohányi Róbert    schedule 07.01.2012
comment
Великолепно! Он работал с почти прямо из коробки. Между этим и статьей, которой поделился @Mechkov, я намного лучше понимаю запросы критериев. Спасибо. - person Alan B. Dee; 07.01.2012
comment
Хороший ответ, помог решить мою проблему со следующей ошибкой You must use dontMaintainCache() - person Jacob; 03.11.2015
comment
Спасибо за ваш пример кода. Я много узнал о запросах критериев jpa с использованием кортежей (вместо сущностей). - person hbobenicio; 01.07.2016
comment
Привет, я хочу создать псевдоним для cb.count(userId) и использовать этот псевдоним в последующем порядке, ссылаясь на псевдоним. Это возможно? - person yaswanth; 01.05.2017
comment
@yaswanth Вы делаете что-то подобное. Вы определяете псевдоним в вызове multiselect. - person Kohányi Róbert; 03.05.2017
comment
@KohányiRóbert, как мне получить доступ к псевдониму позже во время orderBy? - person yaswanth; 03.05.2017
comment
@yaswanth Не уверен, но в примере, который я связал, employee.get("firstName").alias("first") возвращает вам некоторый объект, повторно используйте его в своем предложении orderBy. Хотя не проверял. - person Kohányi Róbert; 03.05.2017

Если вы используете 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"));

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

person Gonzalo    schedule 06.01.2012
comment
Это означает использование Hiberate, что здесь не рассматривается. Вы должны хотя бы упомянуть об этом. - person perissf; 07.01.2012
comment
да, боюсь, я не использую Hibernate. хотя я начинаю желать, чтобы я был! - person Alan B. Dee; 07.01.2012
comment
о, большие извинения. Не понял этого! - person Gonzalo; 07.01.2012

Взгляните на этот простой учебник. Он использует JPA2 и критерии

http://www.jumpingbean.co.za/blogs/jpa2-criteria-api

С уважением!

person Mechkov    schedule 06.01.2012
comment
Опубликованный вами учебник хорош, но запрос, который хочет OP, поднимает интересный вопрос: действительно ли Criteria API полезен, когда запрос возвращает объекты разных типов? - person perissf; 07.01.2012
comment
спасибо, урок мне очень помог. Что касается разных типов, мне нужен только идентификатор (длинный). - person Alan B. Dee; 07.01.2012
comment
Прохладный. Рад, что это помогло. Я сам им пользовался, не так давно. - person Mechkov; 07.01.2012