1. Общий обзор
В этой статье показано, как выполнять сортировку с помощью Hibernate, используя как язык запросов Hibernate (HQL), так и API критериев.
2. Сортировка с помощью HQL
Сортировка с помощью HQL в Hibernate так же проста, как добавление предложения Order By в строку запроса HQL:
String hql = "FROM Foo f ORDER BY f.name";
Query<Foo> query = session.createQuery(hql, Foo.class);
После выполнения этого кода Hibernate сгенерирует следующий SQL-запрос:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_ from
FOO foo0_ order by foo0_.NAME
По умолчанию используется порядок сортировки по возрастанию. Вот почему условие сортировки asc не включено в сгенерированный SQL-запрос.
2.1. Использование явного порядка сортировки
Чтобы указать порядок сортировки вручную, вам нужно будет указать направление сортировки в строке запроса HQL:
String hql = "FROM Foo f ORDER BY f.name ASC";
Query<Foo> query = session.createQuery(hql, Foo.class);
В этом примере установка предложения asc в HQL была включена в сгенерированный SQL-запрос:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_
from FOO foo0_ order by foo0_.NAME ASC
2.2. Сортировка по нескольким признакам
В предложение Order By в строке запроса HQL можно добавить несколько атрибутов вместе с необязательным порядком сортировки:
String hql = "FROM Foo f ORDER BY f.name DESC, f.id ASC";
Query<Foo> query = session.createQuery(hql, Foo.class);
Сгенерированный SQL-запрос изменится соответствующим образом:
Hibernate: select foo0_.ID as ID1_0_, foo0_.NAME as NAME2_0_
from FOO foo0_ order by foo0_.NAME DESC, foo0_.ID ASC
2.3. Установка приоритета сортировки нулевых значений
По умолчанию, если атрибут, по которому выполняется сортировка, имеет значения null, приоритет определяется системой управления доступом (RDMS). Это значение по умолчанию можно изменить, поместив в строку запроса HQL предложение NULLS FIRST или NULLS LAST.
В этом простом примере все пустые значения помещаются в конец списка результатов:
String hql = "FROM Foo f ORDER BY f.name NULLS LAST";
Query<Foo> query = session.createQuery(hql, Foo.class);
Давайте посмотрим на предложение is null then 1 else 0 в сгенерированном SQL-запросе:
Hibernate: select foo0_.ID as ID1_1_, foo0_.NAME as NAME2_1_,
foo0_.BAR_ID as BAR_ID3_1_, foo0_.idx as idx4_1_ from FOO foo0_
order by case when foo0_.NAME is null then 1 else 0 end, foo0_.NAME
2.4. Сортировка отношений "От одного ко Многим"
Давайте проанализируем сложный случай сортировки: сортировка объектов в строке отношения "один ко многим", содержащей коллекцию объектов Foo.
Мы сделаем это, добавив к коллекции аннотацию Hibernate @OrderBy; мы укажем поле, с помощью которого выполняется упорядочивание, а также направление:
@OrderBy(clause = "NAME DESC")
Set<Foo> fooList = new HashSet();
Обратите внимание на аргумент clause в аннотации. Это уникально для @OrderBy в Hibernate по сравнению с аналогичной аннотацией @OrderBy в JPA. Еще одна особенность, отличающая этот подход от его аналога в JPA, заключается в том, что аргумент clause указывает, что сортировка выполняется на основе столбца NAME таблицы FOO, а не на основе атрибута name для Foo.
Теперь давайте рассмотрим фактическую сортировку столбцов Bars и Foos:
String hql = "FROM Bar b ORDER BY b.id";
Query<Bar> query = session.createQuery(hql, Bar.class);
Результирующий оператор SQL показывает, что отсортированные файлы Foo помещаются в fooList:
Hibernate: select bar0_.ID as ID1_0_, bar0_.NAME as NAME2_0_ from BAR bar0_
order by bar0_.ID Hibernate: select foolist0_.BAR_ID as BAR_ID3_0_0_,
foolist0_.ID as ID1_1_0_, foolist0_.ID as ID1_1_1_, foolist0_.NAME as
NAME2_1_1_, foolist0_.BAR_ID as BAR_ID3_1_1_, foolist0_.idx as idx4_1_1_
from FOO foolist0_ where foolist0_.BAR_ID=? order by foolist0_.NAME desc
Следует иметь в виду, что невозможно сортировать списки, как это было в случае с JPA. В документации по Hibernate указано:
“Hibernate currently ignores @OrderBy on @ElementCollection on e.g. List<String>. The order of elements is as returned by the database, undefined.”
“В настоящее время Hibernate игнорирует @OrderBy в @ElementCollection, например, в List<String>. Порядок элементов, возвращаемый базой данных, не определен”.
3. Сортировка По Критериям Гибернации
API объекта Criteria предоставляет класс Order в качестве основного API для управления сортировкой.
3.1. Настройка порядка сортировки
Класс Order имеет два метода для установки порядка сортировки:
- asc(строковый атрибут): Сортирует запрос по атрибуту в порядке возрастания.
- desc(строковый атрибут): Сортирует запрос по атрибуту в порядке убывания.
Давайте начнем с простого примера – сортировки по одному атрибуту id:
CriteriaQuery<Foo> selectQuery = session.getCriteriaBuilder().createQuery(Foo.class);
selectQuery.from(Foo.class);
Query<Foo> query = session.createQuery(selectQuery);
query.setOrder(Collections.singletonList(Order.asc(Foo.class,"id")));
Обратите внимание, что аргумент метода asc чувствителен к регистру и должен совпадать с именем атрибута, по которому выполняется сортировка.
Объектный API Hibernate Criteria явно задает направление сортировки, и это отражается в инструкции SQL, генерируемой кодом:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_
from FOO this_ order by this_.ID sac
3.2. Сортировка по нескольким признакам
Для сортировки по нескольким атрибутам требуется только добавить объект Order в экземпляр Criteria, как в примере ниже:
CriteriaQuery<Foo> selectQuery = session.getCriteriaBuilder().createQuery(Foo.class);
selectQuery.from(Foo.class);
Query<Foo> query = session.createQuery(selectQuery);
query.setOrder(Collections.singletonList(Order.asc(Foo.class,"id")));
Запрос, который генерируется в SQL, является:
Hibernate: select this_.ID as ID1_0_0_, this_.NAME as NAME2_0_0_ from
FOO this_ order by this_.NAME asc, this_.ID sac
3.3. Установка приоритета сортировки нулевых значений
По умолчанию, если атрибут, по которому выполняется сортировка, имеет значения null, то приоритет определяется RDMS. Hibernate Criteria Object API позволяет легко изменить значение по умолчанию и поместить значения null в конец упорядоченного по возрастанию списка:
CriteriaQuery<Foo> selectQuery = session.getCriteriaBuilder().createQuery(Foo.class);
selectQuery.from(Foo.class);
Query<Foo> query = session.createQuery(selectQuery);
List<Order<? super Foo>> orderBy = new ArrayList<>(1);
orderBy.add(Order.by(Foo.class,"name", SortDirection.ASCENDING, NullPrecedence.LAST));
query.setOrder(orderBy);
Вот базовый SQL–запрос - с предложением is null then 1 else 0:
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_,
this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as
ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when
this_.NAME is null then 1 else 0 end, this_.NAME asc
В качестве альтернативы мы также можем поместить нулевые значения в начало упорядоченного по убыванию списка:
CriteriaQuery<Foo> selectQuery = session.getCriteriaBuilder().createQuery(Foo.class);
selectQuery.from(Foo.class);
Query<Foo> query = session.createQuery(selectQuery);
List<Order<? super Foo>> orderBy = new ArrayList<>(2);
orderBy.add(Order.by(Foo.class,"name", SortDirection.ASCENDING, NullPrecedence.FIRST));
query.setOrder(orderBy);
Далее следует соответствующий SQL–запрос - с предложением is null then 0 else 1:
Hibernate: select this_.ID as ID1_1_1_, this_.NAME as NAME2_1_1_,
this_.BAR_ID as BAR_ID3_1_1_, this_.idx as idx4_1_1_, bar2_.ID as
ID1_0_0_, bar2_.NAME as NAME2_0_0_ from FOO order by case when
this_.NAME is null then 0 else 1 end, this_.NAME desc
Обратите внимание, что если атрибут, по которому выполняется сортировка, имеет примитивный тип, например, int, то будет выдано исключение Persistenceexception.
Например, если значение переменной f.an Int равно null, то выполнение запроса будет приостановлено:
String jql = "Select f from Foo as f order by f.anIntVariable desc NULLS FIRST";
Query<Foo> sortQuery = entityManager.createQuery(jql. Foo.class);
бросит:
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException:
Null value was assigned to a property of primitive type setter of
com.cc.jpa.example.Foo.anIntVariable
4. Заключение
В этой статье рассматривается сортировка с помощью Hibernate – с использованием доступных API для простых объектов, а также для объектов в соотношении "один ко многим".
Оригинал статьи: https://www.baeldung.com/hibernate-sort