Hibernate事实:有利于双向集vs列表
Hibernate是一個很棒的ORM工具,它極大地簡化了開發,但是如果您想正確地使用它,則有很多陷阱。
在大中型項目中,雙向父子關聯非常常見,這使我們能夠瀏覽給定關系的兩端。
在控制關聯的持久/合并部分時,有兩個可用選項。 其中將有負責同步收集變化的@OneToMany結束,但是這是一個低效率的做法,這是很好的描述在這里 。
最常見的方法是@ManyToOne端控制關聯并且@OneToMany端使用“ mappedBy”選項時。
我將討論后一種方法,因為就執行的查詢數量而言,這是最常見,最有效的方法。
因此,對于雙向集合,我們可以使用java.util.List或java.util.Set。
根據Hibernate docs的說法,列表和文件包比集合更有效。
但是當我看到以下代碼時,我仍然感到焦慮:
@Entity public class Parent {...@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true) private List children = new ArrayList()public List getChildren() { return children; }public void addChild(Child child) { children.add(child); child.setParent(this); }public void removeChild(Child child) { children.remove(child); child.setParent(null); } }@Entity public class Child {...@ManyToOne private Parent parent;public Parent getParent() { return parent; }public void setParent(Parent parent) { this.parent = parent; } }Parent parent = loadParent(parentId); Child child1 = new Child(); child1.setName("child1"); Child child2 = new Child(); child2.setName("child2"); parent.addChild(child1); parent.addChild(child2); entityManager.merge(parent);這是因為在最近五年中,當在父關聯上調用合并操作時,我一直在插入重復的子代。 發生這種情況是由于以下問題: HHH-3332和HHH-5855 。
我最近一直在測試一些Hibernate版本,并且仍然在3.5.6、3.6.10和4.2.6版本上進行復制。 因此,經過5年在許多項目上看到這一點后,您了解了為什么我對使用列表與集合持懷疑態度。
這是在運行復制此問題的測試用例時得到的結果,因此添加兩個子級,我們得到:
select parent0_.id as id1_2_0_ from Parent parent0_ where parent0_.id=? insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?) insert into Child (id, name, parent_id) values (default, ?, ?)僅當合并操作從父級到子級聯時,才會出現此問題,并且存在以下變通辦法:
- 合并孩子而不是父母
- 在合并父母之前先讓孩子堅持
- 從父級刪除Cascade.ALL或Cascade.MERGE,因為它只影響合并操作,而不影響持久化操作。
但是所有這些都是黑客,在大型項目中很難遵循,因為許多開發人員都在相同的代碼庫上工作。
因此,我的首選方式是使用Set,即使有時它們的效率不如Lists更好,但是由于我一直偏愛正確性與性能優化,因此最好使用Set。
當涉及到這類問題時,最好具有代碼約定,因為它們易于添加到項目開發指南中,并且易于記憶和采用。
使用集合的一個優點是,它迫使您定義適當的equals / hashCode策略(該策略應始終包括實體的業務密鑰。業務密鑰是一種字段組合,該字段組合在父級的子級中是唯一的或唯一的,并且甚至在之前也是一致的)以及將實體持久保存到數據庫中之后)。
如果您擔心會失去以添加孩子的相同順序保存孩子的“列表”功能,那么您仍然可以為Sets模仿。
默認情況下,集合是無序的和未排序的,但是即使您不能對它們進行排序,也可以通過使用@OrderBy JPA注釋按給定的列對它們進行排序,如下所示:
@Entity public class LinkedParent {...@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true) @OrderBy("id") private Set children = new LinkedHashSet();...public Set getChildren() { return children; }public void addChild(LinkedChild child) { children.add(child); child.setParent(this); }public void removeChild(LinkedChild child) { children.remove(child); child.setParent(null); } }加載父級的子級時,生成SQL類似于:
select children0_.parent_id as parent_i3_3_1_, children0_.id as id1_2_1_, children0_.id as id1_2_0_, children0_.name as name2_2_0_, children0_.parent_id as parent_i3_2_0_ from LinkedChild children0_ where children0_.parent_id=? order by children0_.id結論:
如果您的領域模型要求使用列表而不是集合,則將打破您的約束,不允許重復。 但是,如果您需要重復項,則仍然可以使用索引列表。 據說Bag是未排序且“無序的”(即使它按照在數據庫表中添加子的順序來檢索子)。 因此,索引列表也將是一個不錯的選擇,對嗎?
我還想提請注意一個5年的bug,它影響了多個Hibernate版本,并且是我在多個項目中復制的一個版本。 當然,有一些解決方法,例如刪除Cascade.Merge或合并Children vs the Parent,但是有許多開發人員不知道此問題及其解決方法。
根據Hibernate docs:集是“ 表示多值關聯的推薦方法 ”,而且我已經看到很多情況下使用Bags作為默認雙向集合,即使無論如何集都是更好的選擇。
因此,我仍然對Bags保持謹慎,如果我的領域模型強加使用List,我總是會選擇索引的。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2013/10/hibernate-facts-favoring-bidirectional-sets-vs-lists.html
總結
以上是生活随笔為你收集整理的Hibernate事实:有利于双向集vs列表的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 电脑的主板上怎么接线图解(电脑主板接线图
- 下一篇: java常用快捷键大全(java运行快捷
