1. Обзор
В этой статье показано , как выполнять сортировку с помощью Hibernate , используя как язык запросов Hibernate (HQL), так и Criteria API.
2. Сортировка с помощью HQL
Сортировка с помощью Hibernate HQL так же проста, как добавление предложения Order By
в строку запроса HQL:
String hql = "FROM Foo f ORDER BY f.name";
Query query = sess.createQuery(hql);
После выполнения этого кода 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 query = sess.createQuery(hql);
В этом примере установка предложения 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 query = sess.createQuery(hql);
Сгенерированный 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. Настройка приоритета сортировки нулевых значений
По умолчанию, когда атрибут для сортировки имеет нулевые
значения, приоритет определяет RDMS. Эту обработку по умолчанию можно переопределить, поместив предложение NULLS FIRST
или NULLS LAST
в строку запроса HQL .
В этом простом примере любые нули помещаются в конец списка результатов:
String hql = "FROM Foo f ORDER BY f.name NULLS LAST";
Query query = sess.createQuery(hql);
Давайте посмотрим , что в сгенерированном SQL-запросе есть предложение null, затем 1 else 0
: ****
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. Сортировка отношений «один ко многим»
Давайте проанализируем сложный случай сортировки: сортировка сущностей в отношении «один ко многим » — Bar
, содержащий набор сущностей Foo .
Мы сделаем это, добавив в коллекцию аннотацию Hibernate @OrderBy
; укажем поле, по которому осуществляется упорядочивание, а также направление:
@OrderBy(clause = "NAME DESC")
Set<Foo> fooList = new HashSet();
Обратите внимание на этот аргумент предложения в аннотации.
Это уникально для Hibernate @OrderBy
по сравнению с аналогичной аннотацией @OrderBy
JPA. Еще одна характеристика, которая отличает этот подход от его эквивалента JPA, заключается в том, что аргумент предложения
указывает, что сортировка выполняется на основе столбца NAME
таблицы FOO
, а не атрибута имени
Foo
.
Теперь давайте посмотрим на реальную сортировку Bars
и Foos
:
String hql = "FROM Bar b ORDER BY b.id";
Query query = sess.createQuery(hql);
Результирующий оператор 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 игнорирует @OrderBy в @ElementCollection, например, в List<String>. Порядок элементов такой же, как и в базе данных, не определен».
В качестве примечания можно обойти это ограничение, используя устаревшую конфигурацию XML для Hibernate и заменив элемент <List..>
элементом <Bag…>
.
3. Сортировка по критериям гибернации
API объекта Criteria предоставляет класс Order
в качестве основного API для управления сортировкой.
3.1. Установка порядка сортировки
Класс Order
имеет два метода для установки порядка сортировки:
asc (строковый атрибут)
: сортирует запрос поатрибуту
в порядке возрастания.desc (атрибут String)
: сортирует запрос поатрибуту
в порядке убывания.
Начнем с простого примера — сортировки по одному атрибуту id :
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("id"));
Обратите внимание, что аргумент метода asc
чувствителен к регистру и должен соответствовать имени
атрибута для сортировки.
Hibernate Criteria Object API явно задает направление порядка сортировки, и это отражено в операторе 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
, как в примере ниже:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("name"));
criteria.addOrder(Order.asc("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. Настройка приоритета сортировки нулевых значений
По умолчанию, когда атрибут для сортировки имеет нулевые
значения, приоритет определяет RDMS. Hibernate Criteria Object API упрощает изменение этого значения по умолчанию и размещение нулей в конце упорядоченного по возрастанию списка:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.asc("name").nulls(NullPrecedence.LAST));
Вот базовый 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
В качестве альтернативы мы также можем поместить нули в начало упорядоченного списка по убыванию:
Criteria criteria = sess.createCriteria(Foo.class, "FOO");
criteria.addOrder(Order.desc("name").nulls(NullPrecedence.FIRST));
Далее следует соответствующий 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,
будет сгенерировано исключение PresisitenceException
.
Например, если значение f.anIntVariable
равно null, то выполнение запроса:
String jql = "Select f from Foo as f order by f.anIntVariable desc NULLS FIRST";
Query sortQuery = entityManager.createQuery(jql);
выкинет:
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 для простых сущностей, а также для сущностей в отношении «один ко многим».
Реализацию этого руководства по сортировке Hibernate можно найти в проекте github — это проект на основе Eclipse, поэтому его легко импортировать и запускать как есть.