Если отображаемый класс содержит поля типа SortedMap или SortedSet, то их можно пометить аннотацией @SortComparator. И тогда после выборки данных из БД они будут отсортированы с помощью заданного java-компаратора и будут доступны в этих полях уже в отсортированном виде.
Отметим, что при таком подходе сортировка будет проведена в 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
Код
Напишем компаратор, который будет сортировать строки нужным нам образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class EmailComparator implements Comparator<String> { @Override public int compare(String o1, String o2) { String domain1 = o1.split("@")[1]; String domain2 = o2.split("@")[1]; int domainComparison = domain1.compareTo(domain2); return domainComparison == 0 ? o1.compareTo(o2) : domainComparison; } } |
Данный компаратор сортирует адреса электронной почты таким образом, что сначала идут адреса у которых имя домена идёт выше по алфавиту. А если имена доменов совпадают, то внутри такой группы адреса идут просто в алфавитном порядке.
То есть получается что-то вроде: x@a.ru, z@a.ru, a@b.ru, b@b.ru. Домен a.ru выше по алфавиту, поэтому адреса с этим доменом в начале списка.
Далее создадим класс предметной области, содержащий поле типа 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.SortComparator(EmailComparator.class) private SortedSet<String> emails = new TreeSet<>(); //Конструкторы, геттеры и сеттеры, equals(), hashCode() и т.д. } |
Мы помечаем поле SortedSet<String> emails аннотацией @org.hibernate.annotations.SortComparator в которую передаём параметром класс нашего компаратора. Имя аннотации пишем целиком, так как это специфичная 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 |
@SpringBootTest class SpringHibernatePostgresqlApplicationTests { @Autowired PersonRepository personRepository; @Test void sortFetchedWithComparatorTest() throws Exception { SortedSet<String> emails = new TreeSet<>() {{ add("pantyukhina@russia.ru"); add("pantyukhina@mail.ru"); add("irina@russia.ru"); add("irina@mail.ru"); }}; Person irina = new Person("Irina", emails); personRepository.save(irina); Person irinaAfterFetching = personRepository.findPersonWithEmails(irina.getId()); String[] autoSortedEmails = irinaAfterFetching.getEmails().toArray(new String[0]); assertEquals("irina@mail.ru", autoSortedEmails[0]); assertEquals("pantyukhina@mail.ru", autoSortedEmails[1]); assertEquals("irina@russia.ru", autoSortedEmails[2]); assertEquals("pantyukhina@russia.ru", autoSortedEmails[3]); } } |
В данном тесте мы помещаем адреса электронной почты в SortedSet в произвольном порядке. Поскольку SortedSet имплементирован TreeSet’ом, мы знаем, что внутри себя это множество автоматически упорядочивается в простом алфавитном порядке (и мы это увидим). Но после сохранения в БД и выборки, мы видим, что данные отсортированы нашим компаратором, то есть сначала идут адреса с доменом @mail.ru, а затем два адреса с доменом @russia.ru.
Сравним порядок полученных данных с тем, как они хранятся в БД:
И действительно в БД адреса сохранились в простом алфавитном порядке, как и положено данным последовательно взятым из TreeSet<String>. Но при выборке, как мы увидели в тесте, данные сортируются нашим кастомным компаратором.