Hibernate事实:有利于双向集vs列表
Hibernate是一個(gè)很棒的ORM工具,它極大地簡(jiǎn)化了開(kāi)發(fā),但是如果您想正確地使用它,則有很多陷阱。
在大中型項(xiàng)目中,雙向父子關(guān)聯(lián)非常常見(jiàn),這使我們能夠?yàn)g覽給定關(guān)系的兩端。
在控制關(guān)聯(lián)的持久/合并部分時(shí),有兩個(gè)可用選項(xiàng)。 其中將有負(fù)責(zé)同步收集變化的@OneToMany結(jié)束,但是這是一個(gè)低效率的做法,這是很好的描述在這里 。
最常見(jiàn)的方法是@ManyToOne端控制關(guān)聯(lián)并且@OneToMany端使用“ mappedBy”選項(xiàng)時(shí)。
我將討論后一種方法,因?yàn)榫蛨?zhí)行的查詢(xún)數(shù)量而言,這是最常見(jiàn),最有效的方法。
因此,對(duì)于雙向集合,我們可以使用java.util.List或java.util.Set。
根據(jù)Hibernate docs的說(shuō)法,列表和文件包比集合更有效。
但是當(dāng)我看到以下代碼時(shí),我仍然感到焦慮:
@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);這是因?yàn)樵谧罱迥曛?#xff0c;當(dāng)在父關(guān)聯(lián)上調(diào)用合并操作時(shí),我一直在插入重復(fù)的子代。 發(fā)生這種情況是由于以下問(wèn)題: HHH-3332和HHH-5855 。
我最近一直在測(cè)試一些Hibernate版本,并且仍然在3.5.6、3.6.10和4.2.6版本上進(jìn)行復(fù)制。 因此,經(jīng)過(guò)5年在許多項(xiàng)目上看到這一點(diǎn)后,您了解了為什么我對(duì)使用列表與集合持懷疑態(tài)度。
這是在運(yùn)行復(fù)制此問(wèn)題的測(cè)試用例時(shí)得到的結(jié)果,因此添加兩個(gè)子級(jí),我們得到:
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, ?, ?)僅當(dāng)合并操作從父級(jí)到子級(jí)聯(lián)時(shí),才會(huì)出現(xiàn)此問(wèn)題,并且存在以下變通辦法:
- 合并孩子而不是父母
- 在合并父母之前先讓孩子堅(jiān)持
- 從父級(jí)刪除Cascade.ALL或Cascade.MERGE,因?yàn)樗挥绊懞喜⒉僮?#xff0c;而不影響持久化操作。
但是所有這些都是黑客,在大型項(xiàng)目中很難遵循,因?yàn)樵S多開(kāi)發(fā)人員都在相同的代碼庫(kù)上工作。
因此,我的首選方式是使用Set,即使有時(shí)它們的效率不如Lists更好,但是由于我一直偏愛(ài)正確性與性能優(yōu)化,因此最好使用Set。
當(dāng)涉及到這類(lèi)問(wèn)題時(shí),最好具有代碼約定,因?yàn)樗鼈円子谔砑拥巾?xiàng)目開(kāi)發(fā)指南中,并且易于記憶和采用。
使用集合的一個(gè)優(yōu)點(diǎn)是,它迫使您定義適當(dāng)?shù)膃quals / hashCode策略(該策略應(yīng)始終包括實(shí)體的業(yè)務(wù)密鑰。業(yè)務(wù)密鑰是一種字段組合,該字段組合在父級(jí)的子級(jí)中是唯一的或唯一的,并且甚至在之前也是一致的)以及將實(shí)體持久保存到數(shù)據(jù)庫(kù)中之后)。
如果您擔(dān)心會(huì)失去以添加孩子的相同順序保存孩子的“列表”功能,那么您仍然可以為Sets模仿。
默認(rèn)情況下,集合是無(wú)序的和未排序的,但是即使您不能對(duì)它們進(jìn)行排序,也可以通過(guò)使用@OrderBy JPA注釋按給定的列對(duì)它們進(jìn)行排序,如下所示:
@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); } }加載父級(jí)的子級(jí)時(shí),生成SQL類(lèi)似于:
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結(jié)論:
如果您的領(lǐng)域模型要求使用列表而不是集合,則將打破您的約束,不允許重復(fù)。 但是,如果您需要重復(fù)項(xiàng),則仍然可以使用索引列表。 據(jù)說(shuō)Bag是未排序且“無(wú)序的”(即使它按照在數(shù)據(jù)庫(kù)表中添加子的順序來(lái)檢索子)。 因此,索引列表也將是一個(gè)不錯(cuò)的選擇,對(duì)嗎?
我還想提請(qǐng)注意一個(gè)5年的bug,它影響了多個(gè)Hibernate版本,并且是我在多個(gè)項(xiàng)目中復(fù)制的一個(gè)版本。 當(dāng)然,有一些解決方法,例如刪除Cascade.Merge或合并Children vs the Parent,但是有許多開(kāi)發(fā)人員不知道此問(wèn)題及其解決方法。
根據(jù)Hibernate docs:集是“ 表示多值關(guān)聯(lián)的推薦方法 ”,而且我已經(jīng)看到很多情況下使用Bags作為默認(rèn)雙向集合,即使無(wú)論如何集都是更好的選擇。
因此,我仍然對(duì)Bags保持謹(jǐn)慎,如果我的領(lǐng)域模型強(qiáng)加使用List,我總是會(huì)選擇索引的。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2013/10/hibernate-facts-favoring-bidirectional-sets-vs-lists.html
總結(jié)
以上是生活随笔為你收集整理的Hibernate事实:有利于双向集vs列表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑的主板上怎么接线图解(电脑主板接线图
- 下一篇: 进程比线程更多资源_为什么我们不应该使用