Если отображаемый класс содержит поле типа коллекции, поддерживающей порядок элементов, например, List<String> emails, то с помощью аннотации @org.hibernate.annotations.OrderBy можно указать Hibernate’у добавлять в конец запроса на выборку выражение ORDER BY… с произвольным правилом сортировки. В результате данные в такую коллекцию попадут отсортированными ещё в БД.
Отметим, что это произвольное правило может содержать функции БД и вообще практически любой произвольный SQL, что может сделать такой подход зависимым от вендора СУБД.
Подготовка
Создадим базовое веб-приложения на связке Spring Boot 3 + Hibernate + PostgreSQL
Убедитесь, что файле /src/main/resources/application.properties есть следующая строка, позволяющая Hibernate’у автоматически создавать (и обновлять) схему БД при запуске приложения на основании аннотаций в классах предметной области:
spring.jpa.hibernate.ddl-auto=update
Также добавим в application.properties настройку, позволяющую видеть создаваемый Hibernate’ом SQL в консоли:
spring.jpa.show-sql=true
Код
Создадим класс предметной области, содержащий поле типа List<String>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Entity public class Person { @Id @GeneratedValue private Long id; private String name; @ElementCollection @CollectionTable(name = "EMAILS") @Column(name = "EMAIL") @org.hibernate.annotations.OrderBy(clause = "email desc") private List<String> emails = new ArrayList<>(); //Конструкторы, геттеры и сеттеры, equals(), hashCode() и т.д. } |
Мы помечаем поле List<String> emails аннотацией @org.hibernate.annotations.OrderBy с параметром clause, в который передаём выражение, которое должно быть использовано для сортировки данных в БД. Выражение может быть произвольным, но если в нём использовать специфические для конкретной СУБД функции, то это негативно скажется на переносимости приложения.
В нашем случае мы используем выражение EMAILS DESC, чтобы данные этого поля были отсортированы в обратном порядке.
Создадим репозиторий с кастомным запросом, который будет делать выборку из двух таблиц: PERSON и из связанной с ней таблицы EMAILS:
1 2 3 4 5 6 |
@Repository public interface PersonRepository extends JpaRepository<Person, Long> { @Query("select p from Person p left join fetch p.emails where p.id = :id") Person findPersonWithEmails(@Param("id") Long id); } |
Напишем тест, который продемонстрирует работу нашего кода:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@SpringBootTest class SpringHibernatePostgresqlApplicationTests { @Autowired PersonRepository personRepository; @Test void hibernateOrderByTest() throws Exception { Person irina = new Person("Irina", List.of("aaa@mail.ru", "bbb@mail.ru", "ccc@mail.ru")); personRepository.save(irina); Person irinaAfterFetching = personRepository.findPersonWithEmails(irina.getId()); assertEquals("ccc@mail.ru", irinaAfterFetching.getEmails().get(0)); assertEquals("bbb@mail.ru", irinaAfterFetching.getEmails().get(1)); assertEquals("aaa@mail.ru", irinaAfterFetching.getEmails().get(2)); } } |
Мы сохраняем объект irina в БД, предварительно заполнив список List<String> emails адресами электронной почты в алфавитом порядке. После выборки этих данных из БД мы видим, что они оказываются отсортированными в порядке обратном алфавитному.
Обратим внимание на то, как выглядел запрос на выборку:
select p1_0.id,e1_0.person_id,e1_0.email,p1_0.name
from person p1_0 left join emails e1_0 on p1_0.id=e1_0.person_id
where p1_0.id=?
order by e1_0.email desc
В созданном для выборке данных запросы мы видим, что Hibernate добавил выражение order by e1_0.email desc
, что соответствует нашему выражению, переданному в аннотацию @org.hibernate.annotations.OrderBy. Таким образом, сортировка данных произошла в БД.
Сравним порядок полученных данных с тем, как они хранятся в БД:
В БД, как мы и ожидали, данные хранятся в алфавитном порядке. Но при выборке срабатывает выражение из нашей аннотации и получаем мы их в обратном порядке.