Если отображаемый класс содержит поля типа SortedMap или SortedSet, то их можно пометить аннотацией @SortNatural. И тогда после выборки данных из БД, если они, как большинство базовых типов, имплементируют интерфейс Comparable, то они будут отсортированы методом compareTo() и будут доступны в этих полях уже в отсортированном виде.
Отметим, что при таком подходе сортировка будет проведена в java-приложении. Если нужна сортировка выборки на стороне БД, то это делается аннотациями @javax.persistence.annotations.OrderBy или @org.hibernate.annotations.OrderBy.
Подготовка
Создадим базовое веб-приложения на связке Spring Boot 3 + Hibernate + PostgreSQL
Убедитесь, что файле /src/main/resources/application.properties есть следующая строка, позволяющая Hibernate’у автоматически создавать (и обновлять) схему БД при запуске приложения на основании аннотаций в классах предметной области:
spring.jpa.hibernate.ddl-auto=update
Код
Создадим класс предметной области, содержащий поле типа SortedSet<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.SortNatural private SortedSet<String> emails = new TreeSet<>(); //Конструкторы, геттеры и сеттеры, equals(), hashCode() и т.д. } |
Мы помечаем поле SortedSet<String> emails аннотацией @org.hibernate.annotations.SortNatural. Этим мы указываем Hibernate’у сортировать данные этой коллекции при выборке, используя метод compareTo(). Имя аннотации пишем целиком, так как это специфичная Hibernate аннотация, не являющаяся частью JPA.
Создадим репозиторий с кастомным запросом, который будет делать выборку из двух таблиц: 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 19 20 21 22 23 24 25 26 27 28 29 |
@SpringBootTest class SpringHibernatePostgresqlApplicationTests { @Autowired PersonRepository personRepository; @Test void sortFetchedNaturallyTest() throws Exception { Person irina = new Person("Irina"); irina.getEmails().add("ccc@mail.ru"); personRepository.save(irina); Person irinaAfterFirstFetching = personRepository.findPersonWithEmails(irina.getId()); irinaAfterFirstFetching.getEmails().add("bbb@mail.ru"); personRepository.save(irinaAfterFirstFetching); Person irinaAfterSecondFetching = personRepository.findPersonWithEmails(irina.getId()); irinaAfterSecondFetching.getEmails().add("aaa@mail.ru"); personRepository.save(irinaAfterSecondFetching); Person irinaAfterThirdFetching = personRepository.findPersonWithEmails(irina.getId()); String[] autoSortedEmails = irinaAfterThirdFetching.getEmails().toArray(new String[0]); assertEquals("aaa@mail.ru", autoSortedEmails[0]); assertEquals("bbb@mail.ru", autoSortedEmails[1]); assertEquals("ccc@mail.ru", autoSortedEmails[2]); } } |
В данном тесте мы трижды сохраняем объект irina в БД, каждый раз добавляя в поле SortedSet<String> emails новый адрес электронной почты. Причём добавляем адреса в порядке обратном алфавитному: ccc@mail.ru, bbb@mail.ru, aaa@mail.ru.
После выборки данных из БД мы видим, что они оказываются отсортированными в алфавитном порядке. После выборки данных из БД поле SortedSet<String> emails оказывается имплементированным классом PersistentSortedSet<String> и мы имеем сортировку в алфавитном порядке именно потому, что Hibernate использовал стоковый метод compareTo() класса String.
Сравним порядок полученных данных с тем, как они хранятся в БД:
В БД строки лежат в порядке обратном алфавитному, так как мы приложили к этому особые усилия, сделав целых три сохранения объекта irina. Тем не менее при выборке, как мы видели в тесте, эти данные оказываются отсортированы по алфавиту.