Ссылаясь на ранее псевдоним поля в запросе критериев - PullRequest
6 голосов
/ 29 декабря 2010

В этом запросе:

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

// FROM GamePlayedEvent gpe
Root<GamePlayedEvent> gpe = q.from(GamePlayedEvent.class);
// SELECT gameId, COUNT(*) AS count, AVG(duration)
// AS avDur, AVG(rewardCurrency) AS avCur, AVG(rewardXP) avXp
q.select(cb.tuple(
    gpe.<String>get("gameId"),
    cb.count(gpe).alias("count"),
    cb.avg(gpe.<Double>get("duration")).alias("avDur"),
    cb.avg(gpe.<Integer>get("rewardCurrency")).alias("avCur"),
    cb.avg(gpe.<Integer>get("rewardXp")).alias("avXp")
));
// WHERE loginTime BETWEEN ...
q.where(cb.between(gpe.<Date>get("time"), fromTime, toTime));
// GROUP BY gameId
q.groupBy(gpe.<String>get("gameId"));
// ORDER BY count DESC
q.orderBy(cb.desc(???));

Как добавить ORDER BY count DESC, ссылаясь на «количество», определенное в предложении SELECT?

Ответы [ 5 ]

7 голосов
/ 06 сентября 2012

Что если вы просто захватили выражение count и использовали его напрямую?

Expression event_count = cb.count(gpe);

q.select(cb.tuple( 
  gpe.<String>get("gameId"), 
  event_count,
  ... 
)); 

q.orderBy(cb.desc(event_count));
1 голос
/ 11 июня 2011

Несмотря на то, что книга Pro JPA 2 описывает, что метод псевдонима можно использовать для генерации псевдонима SQL-запроса (на странице 251), я не смог заставить его работать ни с EclipseLink, ни с Hibernate. На ваш вопрос я бы сказал, что ваша строка orderBy должна выглядеть так:

q.orderBy(cb.desc(cb.count(gpe));

, если он поддерживается разными поставщиками.

Что касается моих исследований, то кажется, что метод псевдонимов используется только для именования элементов в бугорке, используемом в select (то есть только для проекции).

У меня есть один вопрос. Почему вы хотите использовать API критериев JPA для этого запроса. Он (запрос) выглядит статичным по своей природе, так почему бы не использовать JPQL, где вы можете напрямую определить псевдонимы запроса.

0 голосов
/ 04 марта 2019

Для поля агрегации сумм у меня есть следующий код, который работал для меня:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<T> cq = cb.createQuery(entity);
Root<T> root = cq.from(entity);

cq.orderBy(cb.desc(cb.sum(root.get(orderByString))));

// orderByString is string entity field that is being aggregated and which we want to put in orderby clause as well. 
0 голосов
/ 26 ноября 2016

Сегодня я столкнулся с той же проблемой, но ни одно из предложенных решений не помогло мне, потому что мне нужно было повторно использовать выражение не только в предложении order by, но и в предложении group by.Одним из очевидных решений было бы создание представления на уровне базы данных, но это немного неуклюже, создает ненужный подзапрос и даже невозможно, если пользователю db не предоставлено достаточно привилегий.Лучший вариант, который я реализовал, это написать что-то вроде

q.select(cb.tuple( 
  gpe.<String>get("gameId"), 
  cb.count(gpe),
  ... 
)).groupBy(cb.literal(2)).orderBy(cb.literal(2));

. Первым недостатком этого подхода является то, что код подвержен ошибкам.Другим недостатком является то, что сгенерированный SQL-запрос содержит порядковый номер позиции, который работает в некоторых базах данных (например, postgresql или mariadb), но не работает в других (например, SQL-сервер).В моем случае, однако, я нашел, что это лучший вариант.

Протестировано на jpa 2.1 с hibernate 5.2.3 в качестве провайдера и postgresql 9.6.

0 голосов
/ 04 января 2011

Вы пытались настроить проекцию с псевдонимом?

criteria.setProjection(Projections.projectionList()
   .add(Projections.count("item.id"), "countItems"));
criteria.addOrder(Order.desc("countItems"));
...