假設單向一對多的關聯:
<class name="Library">
<id name="id">
<generator class="sequence"/>
</id>
<set name="visits">
<key column="library_id" not-null="true"/>
<one-to-many class="Visit"/>
</set>
</class>
<class name="Visit">
<id name="id">
<generator class="sequence"/>
</id>
<property name="personName"/>
</class>
在子物件的數量非常龐大時:
問題一、因為是用 Set,所以當增刪子物件時,Hibernate 為了確保 Set 的 unique,必須載入整個 collection,逐一檢查唯一性,出現效能瓶頸。
問題二、改用 List 取代 Set,當增刪子物件時,Hibernate 為了確保 List 的 order,仍舊必須載入整個 collection,效能瓶頸還是在。
問題三、改用 Bag 呢?可以完全避開 Set 的 unique 與 List 的 order,但是每次增刪物件時,主物件的 version number 必須加一,在一對多的關聯中效能影響不大,但是在多對多的關聯中,就無法估計受影響的物件了。
解法:完全不要用關聯!
- 手動建立子物件,並將主物件 id 存於 子物件中。
- 移除子物件時,直接移除子物件即可。
- 移除主物件時,需手動同步移除所有關聯子物件。
- 得自行負責 unique 或者 order。
如果使用單向多對一的關聯,仍舊有上述問題三的狀況出現。
那到底什麼時候可以使用 mapped collections?當 collection 很小時!
延伸討論:
- inverse="true"
- 建議在一對多的父子關聯中一定要使用。
- 可以讓 child 握有關連的主控權。
- 可以減少一次 foreign key 的 insert sql。
- 可以避免 Set 的 unique 檢查。
- 對 Component Collection 無作用。
- 盡量不要呼叫 lazy 的 getChildred()。
- 一定要實做 equals() 和 hashcode()
- Hibernate 用到的物件都要實做 equals() 和 hashcode(),包括 Value Type object。
- 如果沒有適當地 property 可供實做 equals() 和 hashcode(),使用 uuid 之類的 surrogate key。
- 在沒有實做 equals() 和 hashcode() 的情況下,Hibernate 要新增一個物件,假設 db 裡已經有同一個物件存在,因為沒有實做 equals() 和 hashcode() ,所以 Hibernate 認為 db 無該物件存在,直接執行 insert,而且可能執行 delete 刪除 db 裡的舊物件。
- Hibernate 知道的愈多,愈能有效率的運作。
- 使用 version
- Entity 一定要使用 version。
- 使用 version 可以減少 Hibernate 往返 db 的次數。
- Eager Fetch
- 不建議使用 fetch=select,會造成 N+1 Select。
- 建議使用 fetch=join,可以在同一個 sql 取得 parent 和 children。
- 更建議使用:lazy by default, eager fetch on demand。
- 參考:到底是誰用了fetch="select"! 。
- Value Type object 使用 Surrogate Key
- 因為 Hibernate 會以 parent 的 primary key 與 Value Type object 的所有 not null property 作為Value Type object 的 primary key,這會讓 pk 變得很奇怪,尤其是當 not null property 有 Date 型別時,因為 Date 包含到毫秒,所以可能讓應該相同的 Value Type object 因為毫秒的差異變成不同,也會影響到 db 的 index 效能。
- 建議使用 Surrogate Key 做為 Value Type object 裡唯一 not null property,如此可以改善 db 效能。
- 但是,這個 Surrogate Key 主要用來改善 db 效能,盡量不要用在 equals() 和 hashcode() 裡。
沒有留言:
張貼留言