01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@Entity public class Starship { @Id @GeneratedValue (strategy=GenerationType.SEQUENCE) private Long id; public Long getId() { return id;} protected void setId(Long id) { this .id = id;} @OneToMany (mappedBy= "starship" , cascade={CascadeType.ALL}) private List<Officer> officers = new ArrayList<Officer>(); public List<Officer> getOfficers() { return Collections.unmodifiableList(officers);} protected void setOfficers(List<Officer> officers) { this .officers = officers;} public void addOfficer(Officer officer) { officer.setStarship( this ); this .officers.add(officer); } //more code } @Entity public class Officer { @Id @GeneratedValue (strategy=GenerationType.SEQUENCE) private Long id; public Long getId() { return id;} protected void setId(Long id) { this .id = id;} @ManyToOne private Starship starship; public Starship getStarship() { return starship;} protected void setStarship(Starship starship) { this .starship = starship;} //more code } |
Теперь у нас есть следующее требование:
Мы назначим всех офицеров на звездолет в алфавитном порядке.
Для решения этого требования мы можем:
- реализация HQL- запроса с предложением order by .
- используя сортировку
- используя порядок заказа .
Первое решение хорошо с точки зрения производительности, но подразумевает большую работу в качестве разработчиков, потому что мы должны написать запрос, чтобы найти всех офицеров данного звездолета, упорядоченных по имени, а затем создать метод поиска в слое DAO (в случае, если вы используете шаблон DAO ) ,
Давайте рассмотрим второе решение, мы могли бы использовать класс SortedSet в качестве ассоциации и сделать Officer реализуемым Comparable , чтобы у Officer было естественный порядок. Это решение подразумевает меньше работы, чем первое, но требует использования аннотации @Sort hibernate для определения ассоциации. Итак, давайте изменим предыдущую модель, чтобы она соответствовала нашему новому требованию. Обратите внимание, что в спецификации JPA нет эквивалентной аннотации.
Сначала мы собираемся реализовать
01
02
03
04
05
06
07
08
09
10
11
|
@Entity public class Officer implements Comparable<Officer>{ //getters, setters, equals, ... code public int compareTo(Officer officer) { return this .name.compareTo(officer.getName()); } } |
Сопоставимый интерфейс в классе Officer .
Мы заказываем офицера по имени, просто сравнивая имя поля. Следующим шагом является аннотирование ассоциации с @Sort .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Entity public class Starship { //more code @OneToMany (mappedBy= "starship" , cascade={CascadeType.ALL}) @Sort (type=SortType.NATURAL) private SortedSet>Officer< officers = new TreeSet>Officer<(); public SortedSet>Officer< getOfficers() { return Collections.unmodifiableSortedSet(officers);} protected void setOfficers(SortedSet>Officer< officers) { this .officers = officers;} public void addOfficer(Officer officer) { officer.setStarship( this ); this .officers.add(officer); } } |
Обратите внимание, что теперь ассоциация офицеров реализована с использованием SortedSet вместо List . Кроме того, мы добавляем аннотацию @Sort к отношениям, заявляя, что офицеры должны быть в естественном порядке. Перед тем, как закончить этот пост, мы будем настаивать больше в теме @Sort , но пока этого достаточно.
И, наконец, метод, позволяющий упорядочить всех офицеров данного звездолета по имени и распечатать их в лог-файле.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
EntityManager entityManager = this .entityManagerFactory.createEntityManager(); EntityTransaction transaction = entityManager.getTransaction(); transaction.begin(); log.info( "Before Find Starship By Id" ); Starship newStarship = entityManager.find(Starship. class , starshipId); SortedSet<Officer> officers = newStarship.getOfficers(); for (Officer officer : officers) { log.info( "Officer name {} with rank {}" , officer.getName(), officer.getRank()); } log.info( "After Find Starship By Id and Before Commit" ); transaction.commit(); entityManager.close(); |
Все офицеры отсортированы по именам, но давайте рассмотрим, какие запросы отправляются в СУБД .
1
2
3
4
5
|
Hibernate: select starship0_. id as id1_0_, starship0_.affiliationEnum as affiliat2_1_0_, starship0_.launched as launched1_0_, starship0_.height as height1_0_, starship0_.length as length1_0_, starship0_.power as power1_0_, starship0_.width as width1_0_, starship0_.registry as registry1_0_, starship0_.starshipClassEnum as starship9_1_0_ from Starship starship0_ where starship0_. id =? Hibernate: select officers0_.starship_id as starship7_1_1_, officers0_. id as id1_, officers0_. id as id0_0_, officers0_.affiliationEnum as affiliat2_0_0_, officers0_.homePlanet as homePlanet0_0_, officers0_.name as name0_0_, officers0_.rank as rank0_0_, officers0_.speciesEnum as speciesE6_0_0_, officers0_.starship_id as starship7_0_0_ from Officer officers0_ where officers0_.starship_id=? |
Первый запрос является результатом вызова метода find на экземпляре EntityManager для поиска звездолета.
Поскольку отношения один ко многим по умолчанию являются ленивыми, когда мы вызываем метод getOfficers и мы впервые обращаемся к SortedSet , выполняется второй запрос для получения всех сотрудников. Обратите внимание, что в запросе нет порядка по пунктам, но, внимательно изучив выходные данные, сотрудники извлекаются в алфавитном порядке.
1
2
3
4
5
6
7
|
<Officer name Beverly Crusher with rank COMMANDER> <Officer name Data with rank LIEUTENANT_COMMANDER> <Officer name Deanna Troi with rank COMMANDER> <Officer name Geordi La Forge with rank LIEUTENANT> <Officer name Jean-Luc Picard with rank CAPTAIN> <Officer name William Riker with rank COMMANDER> <Officer name Worf with rank LIEUTENANT> |
Так кто же сортирует офицерские сущности? Объяснение на аннотации @Sort . В hibernate отсортированная коллекция сортируется в памяти, так как Java отвечает за сортировку данных с помощью метода CompareTo .
Очевидно, что этот метод не лучший способ производительности для сортировки коллекции элементов. Вероятно, нам понадобится гибридное решение между использованием предложения SQL и использованием аннотации вместо написания запроса.
И это приводит нас к объяснению третьей возможности, используя подход к упорядочению. Аннотация @OrderBy , доступная как аннотация hibernate и аннотация JPA , позволяет нам указать, как упорядочить коллекцию, добавив предложение « order by » в сгенерированный SQL .
Имейте в виду, что использование javax.persistence.OrderBy позволяет нам определять порядок коллекции с помощью свойств объекта, в то время как org.hibernate.annotations.OrderBy упорядочивает коллекцию, добавляя непосредственно фрагмент SQL (не HQL ) для упорядочения по предложению.
Теперь к классу Officer не нужно прикасаться, нам не нужно реализовывать метод CompareTo или java.util.Comparator . Нам нужно только аннотировать поле офицеров аннотацией @OrderBy . Поскольку в этом случае мы упорядочиваем по простому атрибуту, аннотация JPA используется для обеспечения полной совместимости с другими механизмами ORM, « готовыми к JPA ». По умолчанию предполагается восходящий порядок.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@Entity public class Starship { //code @OneToMany (mappedBy= "starship" , cascade={CascadeType.ALL}) @OrderBy ( "name" ) private List<Officer> officers = new ArrayList<Officer>(); public List<Officer> getOfficers() { return Collections.unmodifiableList(officers);} protected void setOfficers(List<Officer> officers) { this .officers = officers;} public void addOfficer(Officer officer) { officer.setStarship( this ); this .officers.add(officer); } } |
И если мы перезапустим метод get all employee, будут отправлены следующие запросы:
1
2
3
4
5
|
Hibernate: select starship0_.id as id1_0_, starship0_.affiliationEnum as affiliat2_1_0_, starship0_.launched as launched1_0_, starship0_.height as height1_0_, starship0_.length as length1_0_, starship0_.power as power1_0_, starship0_.width as width1_0_, starship0_.registry as registry1_0_, starship0_.starshipClassEnum as starship9_1_0_ from Starship starship0_ where starship0_.id=? Hibernate: select officers0_.starship_id as starship7_1_1_, officers0_.id as id1_, officers0_.id as id0_0_, officers0_.affiliationEnum as affiliat2_0_0_, officers0_.homePlanet as homePlanet0_0_, officers0_.name as name0_0_, officers0_.rank as rank0_0_, officers0_.speciesEnum as speciesE6_0_0_, officers0_.starship_id as starship7_0_0_ from Officer officers0_ where officers0_.starship_id=? order by officers0_.name asc |
Оба запроса все еще выполняются, но имейте в виду, что теперь запрос select также содержит предложение order by .
С этим решением вы экономите время процесса, позволяя быстрой сортировке данных СУБД , а не упорядочиваете данные в Java после их получения.
Кроме того, аннотация OrderBy не заставляет вас использовать коллекцию SortedSet или SortedMap . Вы можете использовать любую коллекцию, такую как HashMap , HashSet или даже Bag , потому что hibernate будет внутренне использовать LinkedHashMap , LinkedHashSet или ArrayList соответственно.
В этом примере мы увидели важность правильного выбора стратегии заказа. По возможности, вы должны стараться использовать возможности СУБД , поэтому первым вариантом будет использование аннотации OrderBy ( hibernate или JPA ) вместо сортировки . Но иногда оговорки OrderBy будет недостаточно. В этом случае я рекомендую вам использовать аннотацию Sort с пользовательским типом (используя класс java.util.Comparator ) вместо того, чтобы использовать естественный порядок, чтобы не касаться классов моделей.
1
|
@Sort (type=SortType.COMPARATOR, comparator=TimeComparator. class ) |
Я хотел бы, чтобы этот пост помог вам понять разницу между «сортировать» и «порядок» в спящем режиме .
Продолжай учиться.
Ссылка: Hibernate Совет: сортировка и заказ от нашего партнера JCG Алекса Сото в блоге One Jar To Rule All .