@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
}
現在我們有下一個要求:
我們將按字母順序將所有軍官分配給星際飛船。
為了解決這個要求,我們可以:
- 使用order by子句實現HQL查詢。
- 使用排序方法。
- 使用訂單方法。
第一個解決方案在性能方面不錯,但是作為開發人員意味著更多的工作,因為我們應該編寫一個查詢來查找按名稱排序的給定飛船的所有人員,然后在DAO層中創建finder方法(如果您使用的是DAO模式 ) 。
讓我們探索第二個解決方案,我們可以使用SortedSet類作為關聯,并使Officer實現Comparable ,因此Officer具有 自然秩序。 該解決方案的工作量少于第一個,但需要在關聯定義上使用@Sort 休眠注釋,因此讓我們修改以前的模型以滿足我們的新要求。請注意, JPA規范中沒有等效的注釋。 首先我們要實施
@Entity
public class Officer implements Comparable<Officer>{//getters, setters, equals, ... codepublic int compareTo(Officer officer) {return this.name.compareTo(officer.getName());}}
官員類中的可比接口。
我們通過簡單地比較名稱字段來按名稱訂購人員。 下一步是使用@Sort注釋關聯。
@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主題中堅持使用更多內容,但到目前為止已經足夠。
最后是一種方法,該方法可以按名稱對給定星際飛船的所有人員進行排序,并將其打印在日志文件中。
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();
所有人員均按其姓名排序,但讓我們檢查將哪些查詢發送到RDBMS 。
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=?
第一個查詢是在EntityManager實例查找星艦上調用find方法導致的。
因為默認情況下,當我們調用getOfficers方法并且第一次訪問SortedSet時 ,一對多關系是惰性的,所以執行第二個查詢來檢索所有人員。 看到查詢中不存在order by子句,但仔細查看輸出,會按字母順序檢索人員。
<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注釋上。 在休眠狀態下,一個排序的集合在Java內存中排序,它負責使用compareTo方法對數據進行排序。
顯然,此方法不是對元素集合進行排序的最佳性能方法。 在使用SQL子句和使用注釋而不是編寫查詢之間,我們可能需要一種混合解決方案。
這使我們使用排序方法來解釋第三種可能性。 @OrderBy注釋(可以用作休眠注釋和JPA注釋),讓我們指定如何通過在生成的SQL中添加“ order by ”子句來對集合進行排序 。
請記住,使用javax.persistence.OrderBy允許我們通過對象屬性指定集合的??順序,同時org.hibernate.annotations.OrderBy對集合進行排序,將SQL的片段(不是HQL )直接附加到order by子句中。
現在不應該觸動Officer類,我們不需要實現compareTo方法或java.util.Comparator 。 我們只需要使用@OrderBy注釋來注釋人員字段。 由于在這種情況下,我們通過簡單的屬性進行排序,因此使用JPA注釋來保持與其他“支持JPA的 ” ORM引擎的完全兼容性。 默認情況下,假定升序。
@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);}
}
如果我們重新運行所有人員的方法,則會發送下一個查詢:
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子句。
使用此解決方案,您可以節省處理時間,從而允許RDBMS快速對數據進行排序,而不是一旦接收到Java中的數據就對其進行排序。
此外, OrderBy批注不會強制您使用SortedSet或SortedMap集合。 您可以使用HashMap , HashSet甚至Bag之類的任何集合,因為hibernate將在內部分別使用LinkedHashMap , LinkedHashSet或ArrayList 。
在這個例子中,我們已經看到了正確選擇訂購策略的重要性。 只要有可能,您都應該嘗試利用RDBMS的功能,因此您的第一個選擇應該是使用OrderBy注釋( 休眠或JPA ),而不是Sort 。 但是有時OrderBy子句是不夠的。 在這種情況下,我建議您使用具有自定義類型的Sort注釋(使用java.util.Comparator類),而不是按自然順序進行中繼以避免觸摸模型類。
@Sort(type=SortType.COMPARATOR, comparator=TimeComparator.class)
我希望這篇文章可以幫助您了解休眠狀態下 “排序”和“順序”之間的區別。
保持學習。
參考: Hibernate提示:我們的JCG合作伙伴 Alex Soto的“ 排序和排序”在One Jar To Rule All All博客中。
翻譯自: https://www.javacodegeeks.com/2012/04/hibernate-tip-sort-and-order.html