«1. Обзор

В этой статье показано, как выполнять сортировку с помощью Hibernate, используя как язык запросов Hibernate (HQL), так и Criteria API.

2. Сортировка с помощью HQL

Сортировка с помощью HQL Hibernate так же проста, как добавление предложения 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);

Давайте посмотрим на предложение 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. Сортировка отношений «один ко многим»

Давайте проанализируем сложный случай сортировки: сортировка сущностей в отношении «один ко многим» — Bar, содержащий набор сущностей Foo.

Мы сделаем это, добавив в коллекцию аннотацию Hibernate @OrderBy; мы укажем поле, по которому выполняется упорядочение, а также направление:

@OrderBy(clause = "NAME DESC")
Set<Foo> fooList = new HashSet();

Обратите внимание на этот аргумент в аннотации. Это уникально для @OrderBy Hibernate по сравнению с аналогичной аннотацией @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 currently ignores @OrderBy on @ElementCollection on e.g. List<String>. The order of elements is as returned by the database, undefined.”

В качестве примечания можно обойти это ограничение, используя устаревшую конфигурацию XML для Hibernate и заменив элемент \u003cList..\u003e элементом \u003cBag…\u003e.

3. Сортировка с помощью критериев гибернации

API объектов критериев предоставляет класс Order в качестве основного API для управления сортировкой.

3.1. Установка порядка сортировки

Класс Order имеет два метода для установки порядка сортировки:

    asc(String attribute) : Сортирует запрос по атрибуту в возрастающем порядке. desc(String attribute) : сортирует запрос по атрибуту в порядке убывания.

Начнем с простого примера — сортировки по одному атрибуту 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, поэтому его должно быть легко импортировать и запускать как есть.