API-интерфейс Hibernate Criteria - HAVING работает вокруг

Я написал запрос с использованием Hibernate Criteria API, чтобы получить суммирование определенного значения, теперь мне нужно ограничить результат строками, где эта сумма больше или равна определенному значению.

Обычно я бы использовал предложение HAVING в своем SQL для этого, но API-интерфейс Criteria API, похоже, не поддерживает это в данный момент.

В сыром SQL это то, что мне нужно:

SELECT user_pk, sum(amount) as amountSum
FROM transaction
GROUP BY user_pk
HAVING amountSum >=50;

Одна работа, о которой я думал, заключается в использовании подзапроса в предложении FROM, который захватывает это суммирующее значение и использует внешний запрос для ограничения его с помощью предложения WHERE.

Итак, в raw SQL он будет выглядеть примерно так:

SELECT user_pk, amountSum
FROM (SELECT user_pk, sum(amount) as amountSum
      FROM transaction
      GROUP BY user_pk)
WHERE amountSum > 50;

Может ли кто-нибудь указать мне в правильном направлении, как я могу написать это, используя API критериев или любые другие предложения/варианты работы, которые я могу использовать для решения проблемы HAVING?

Это код API критериев, который у меня есть для приведенного выше примера

DetachedCriteria criteria = DetachedCriteria.forClass(Transaction.class,"transaction");
criteria.setProjection(criteria.setProjection(Projections.projectionList().add(
        Projections.groupProperty("user.userPK").as("user_pk")).add(
            Projections.sum("transaction.amount").as("amountSum")));

Спасибо!

Ответ 1

Я не знаю, как Hibernate/NHibernate использовать подзапрос в предложении FROM, но вы можете использовать их в предложении WHERE. Извиняюсь за любые ошибки кода Java/Hibernate, я больше знаком с С#/NHibernate.


DetachedCriteria subQuery = DetachedCriteria.forClass(Transaction.class);
subQuery.setProjection(Projections.sum("amount"));
subQuery.add(Expression.eqProperty("userPk", "tOuter.userPk"));

DetachedCriteria outerQuery = DetachedCriteria.forClass(Transaction.class, "tOuter");
outerQuery.setProjection(Projections.projectionList()
    .Add(Projections.sum("amount").as("sumAmount"))
    .Add(Projections.groupProperty("userPk").as("user_pk"));
outerQuery.add(Subqueries.le(50, subQuery));


Этот код должен привести к тому, что SQL похож на:


SELECT tOuter.userPk as user_pk, sum(tOuter.amount) as sumAmount
FROM transaction tOuter
WHERE 50 <= (SELECT sum(amount) FROM transaction WHERE userPk = tOuter.userPk)
GROUP BY tOuter.userPk

Недостатком этого подхода является то, что он вычисляет каждую из сумм дважды, это может иметь пагубное влияние на производительность в зависимости от объема данных - в этом случае вы захотите использовать запрос HQL, который поддерживает HAVING пункт.

Ответ 2

HHH-1700 был отмечен как дубликат HHH-1043 и поэтому не будет исправлен. Вы найдете решение и обходное решение, а также другие народы, на HHH-1043.