В проекте, использующем Hibernate 6.4, я использую Criteria API, предоставляемый через API персистентности Jakarta (зависимость от Hibernate). Однако я столкнулся с ошибкой при использовании псевдонимов в функциях агрегирования при выборе.
Предположим, у нас есть сущность MyEntity со следующими атрибутами:
id
типа Integer (для этой задачи он нам не нужен)attr1
типа Stringattr2
типа Целое числоБудем считать, что аннотированный класс правильно написан согласно этому описанию и правильно импортирован во время выполнения приложения.
Здесь я хочу выбрать значения attr1
и сумму значений attr2
из таблицы, связанной с этой сущностью, и упорядочить результат по сумме значений attr2
.
Вот код, соответствующий тому, что я написал в своем проекте (точный код дать не могу, поэтому проиллюстрирую то, что я сделал, на этом примере):
public class MyClass {
public List<Object[]> myMethod(Session session, String orderAttribute) {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<?> cq = cb.createQuery(Object[].class);
Root<MyEntity> root = cq.from(MyEntity.class);
List<Selection<?>> selections = new ArrayList<>();
selections.add(root.get("attr1"));
Selection<Number> sumAttr2 = cb.sum(root.get("attr2")).alias("sumAttr2");
selections.add(sumAttr2);
cq.multiselect(selections);
cq.orderBy(cb.asc(root.get(orderAttribute)));
Query<Object[]> query = session.createQuery(cq);
List<Object[]> result = query.getResultList();
return result;
}
}
Когда я выполняю это с помощью orderAttribute = "attr1"
, все работает как положено.
Но когда я выполняю запрос с помощью orderAttribute = "sumAttr2"
, он терпит неудачу и выдает следующую ошибку:
org.hibernate.query.sqm.PathElementException: Could not resolve attribute 'sumAttr2' of 'com.example.MyClass'
У меня нет SQL-запроса, сгенерированного во время ошибки, потому что, похоже, он дает сбой при создании запроса, а не во время его выполнения. Однако у меня есть запрос случая orderAttribute = "attr1"
, который соответствует этому:
select me1_0.attr1,sum(me1_0.attr2) from my_entity me1_0 order by 1
Я предполагаю, что проблема связана с псевдонимом, потому что он не отображается в запросе (у меня нет sum(me1_0.attr2) as sumAttr2
), и когда я пытаюсь упорядочить результат по этому отсутствующему псевдониму, у меня возникает ошибка, потому что API " думаю», это должно быть атрибутом сущности.
Я пробовал несколько способов написать псевдоним, но ни один из них не сработал.
Selection<Number> sumAttr2 = cb.sum(root.get("attr2")).alias("sumAttr2");
selections.add(sumAttr2);
Expression<Number> sumAttr2 = criteriaBuilder.sum(root.get("attr2"));
Selection<Number> sumAttr2Selection = sumAttr2.alias("sumAttr2");
selections.add(sumAttr2Selection);
Expression<Number> sumAttr2 = criteriaBuilder.sum(root.get("attr2"));
sumAttr2.alias("sumAttr2");
selections.add(sumAttr2)
Вы можете помочь мне ? Я хочу получить что-то эквивалентное следующему запросу, используя Criteria API:
select me1_0.attr1,sum(me1_0.attr2) as sumAttr2 from my_entity me1_0 order by sumAttr2
Хорошо, я нашел решение: нам нужно использовать объект Expression, возвращаемый методом CriteriaBuilder.sum()
.
Вот моя реализация:
public class MyClass {
public List<Object[]> myMethod(Session session, String orderAttribute) {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<?> cq = cb.createQuery(Object[].class);
Root<MyEntity> root = cq.from(MyEntity.class);
// Selections
List<Selection<?>> selections = new ArrayList<>();
selections.add(root.get("attr1"));
Expression<Number> sumAttr2 = criteriaBuilder.sum(root.get("attr2"));
Selection<Number> sumAttr2Selection = sumAttr2.alias("sumAttr2");
selections.add(sumAttr2Selection);
cq.multiselect(selections);
// End of selections
// Order
Expression<?> sortIndex;
switch (criteres.getColonneTri()) {
case "sumAttr2":
sortIndex = somme;
break;
// Here, other alias cases if any.
default:
sortIndex = root.get(orderAttribute);
break;
}
cq.orderBy(cb.asc(sortIndex));
// End of order
Query<Object[]> query = session.createQuery(cq);
List<Object[]> result = query.getResultList();
return result;
}
}
И соответствующий SQL-запрос, сгенерированный Hibernate/JPA:
select me1_0.attr1,sum(me1_0.attr2) from my_entity me1_0 order by 2
Здесь следует отметить две вещи:
alias()
, согласно документу со спецификациями Jakarta Persistence (версия 3.1), применит заданный псевдоним к результату запроса при выполнении. Он не применяет псевдоним непосредственно к SQL-запросу.