理解Hibernate集合性能技術
本段中,我們將著重講述Hibernate集合性能(Understanding Collection performance)集合在運行時的事宜。包括Hibernate集合性能(Understanding Collection performancee)技術的分類、Lists, maps 和sets用于更新效率最高、Bag和list是反向集合類中效率最高的。
1.Hibernate集合性能分類(Taxonomy),Hibernate定義了三種基本類型的集合:
◆值數據集合
◆一對多關聯
◆多對多關聯
這個分類是區分了不同的表和外鍵關系類型,但是它沒有告訴我們關系模型的所有內容。 要完全理解他們的關系結構和性能特點,我們必須同時考慮“用于Hibernate更新或刪除集合行數據的主鍵的結構”。 因此得到了如下的分類:
◆有序集合類
◆集合(sets)
◆包(bags)
所有的有序集合類(maps, lists, arrays)都擁有一個由
集合(sets)的主鍵由
Bag是最差的。因為bag允許重復的元素值,也沒有索引字段,因此不可能定義主鍵。 Hibernate無法判斷出重復的行。當這種集合被更改時,Hibernate將會先完整地移除 (通過一個(in a single DELETE))整個集合,然后再重新創建整個集合。 因此Bag是非常低效的。
注意:對于一對多關聯來說,“主鍵”很可能并不是數據庫表的物理主鍵。 但就算在此情況下,上面的分類仍然是有用的。(它仍然反映了Hibernate在集合的各數據行中是如何進行“定位”的。)
2. Lists, maps 和sets用于更新效率最高
根據我們上面的討論,顯然有序集合類型和大多數set都可以在增加、刪除、修改元素中擁有最好的性能。
可論證的是對于多對多關聯、值數據集合而言,有序集合類比集合(set)有一個好處。因為Set的內在結構, 如果“改變”了一個元素,Hibernate并不會更新(UPDATE)這一行。 對于Set來說,只有在插入(INSERT)和刪除(DELETE) 操作時“改變”才有效。再次強調:這段討論對“一對多關聯”并不適用。
注意到數組無法延遲載入,我們可以得出結論,list, map和idbags是最高效的(非反向)集合類型,set則緊隨其后。 在Hibernate中,set應該時最通用的集合類型,這時因為“set”的語義在關系模型中是最自然的。
但是,在設計良好的Hibernate領域模型中,我們通常可以看到更多的集合事實上是帶有inverse="true" 的一對多的關聯。對于這些關聯,更新操作將會在多對一的這一端進行處理。因此對于此類情況,無需考慮其集合的更新性能。
3. Bag和list是反向集合類中效率最高的
在把bag扔進水溝之前,你必須了解,在一種情況下,bag的性能(包括list)要比set高得多: 對于指明了inverse="true"的集合類(比如說,標準的雙向的一對多關聯), 我們可以在未初始化(fetch)包元素的情況下直接向bag或list添加新元素! 這是因為Collection.add())或者Collection.addAll() 方法 對bag或者List總是返回true(這點與與Set不同)。因此對于下面的相同代碼來說,速度會快得多。
- Parent p = (Parent) sess.load(Parent.class, id);
- Child c = new Child();
- c.setParent(p);
- p.getChildren().add(c); //no need to fetch the collection!
- sess.flush();
4. 一次性刪除(One shot delete)
偶爾的,逐個刪除集合類中的元素是相當低效的。Hibernate并沒那么笨, 如果你想要把整個集合都刪除(比如說調用list.clear()),Hibernate只需要一個DELETE就搞定了。
假設我們在一個長度為20的集合類中新增加了一個元素,然后再刪除兩個。 Hibernate會安排一條INSERT語句和兩條DELETE語句(除非集合類是一個bag)。 這當然是顯而易見的。
但是,假設我們刪除了18個數據,只剩下2個,然后新增3個。則有兩種處理方式:
逐一的刪除這18個數據,再新增三個;
刪除整個集合類(只用一句DELETE語句),然后增加5個數據。
Hibernate還沒那么聰明,知道第二種選擇可能會比較快。 (也許讓Hibernate不這么聰明也是好事,否則可能會引發意外的“數據庫觸發器”之類的問題。)
幸運的是,你可以強制使用第二種策略。你需要取消原來的整個集合類(解除其引用), 然后再返回一個新的實例化的集合類,只包含需要的元素。有些時候這是非常有用的。
顯然,一次性刪除并不適用于被映射為inverse="true"的集合。
【編輯推薦】