Перейти к основному содержимому

Сортировка с помощью Hibernate

· 6 мин. чтения

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, поэтому его легко импортировать и запускать как есть.