使用NHibernate實現一對多,多對一的關聯很是簡單,可如果要用複合主鍵實現確實讓人有些淡淡的疼。雖然很淡疼但還是要去抹平這個坑,在下不才,願意嘗試。 以示例進入正文,源碼下載地址: 一、數據表關係圖 很明顯,他是一個自引用數表,實現無限級樹結構的存儲。 二、關鍵步驟 註解如何實現複合主鍵 根據 ...
使用NHibernate實現一對多,多對一的關聯很是簡單,可如果要用複合主鍵實現確實讓人有些淡淡的疼。雖然很淡疼但還是要去抹平這個坑,在下不才,願意嘗試。
以示例進入正文,源碼下載地址:
一、數據表關係圖
很明顯,他是一個自引用數表,實現無限級樹結構的存儲。
二、關鍵步驟
- 註解如何實現複合主鍵
根據官方文檔說明,聯合主鍵最好是一個獨立的類,需要重載Equals和GetHashCode方法,且標記為可序列化。代碼如下:
[Serializable] public class BaseInfo { public virtual string Id { get; set; } public virtual string GroupNumber { get; set; } public override bool Equals(object obj) { var baseInfo = obj as BaseInfo; if (baseInfo == null) { return false; } return baseInfo.Id == this.Id && baseInfo.GroupNumber == this.GroupNumber; } public override int GetHashCode() { return base.GetHashCode(); } }
- 子類配置好聯合主鍵
[CompositeId(0, Name = "BN")] [KeyProperty(1, Name = "Id", Column = "Id", TypeType = typeof(string))] [KeyProperty(2, Name = "GroupNumber", Column = "GroupNumber", TypeType = typeof(string))] public virtual BaseInfo BN { get; set; }
說明:
1.實現為引用BaseInfo類,而不是繼承.
- 實現一對 和 多對一的映射
這步沒有多大難度,主要處理好註解的順序即可,以及OneToMany時聯合主鍵如何設置的問題.示例代碼如下:
[Bag(0, Name = "Childs", Cascade = "all", Lazy = CollectionLazy.False, Inverse = true)] [Key(1)] [Column(2, Name = "ParentId")] [Column(3, Name = "GroupNumber")] [OneToMany(4, ClassType = typeof(Foo))] public virtual IList<Foo> Childs { get; set; } [ManyToOne(0, Name = "Parent", ClassType = typeof(Foo))] [Column(1, Name = "ParentId")] [Column(2, Name = "GroupNumber")] public virtual Foo Parent { get; set; }
三、出錯了,有Bug
- childs沒有數據
重載的GetHashCode方法有問題,返回值應該是聯合主鍵HashCode,優化後的實現如下:
public override int GetHashCode() { return (this.Id + "|" + this.GroupNumber).GetHashCode(); //判斷緩存是否存在,已此作為Key }
- 插入數據時報錯,提示SqlParameterCollection的索引無效[索引溢出錯誤]
原因,最初在設計Parent的時候,與聯合主鍵共用了一個欄位GroupNumber,導致在NHibernate做映射轉換的時候會多計算出一個需要填充的值,但SqlParameterCollection中又少一個位置。優化代碼如下:
//外鍵與聯合主鍵不要共用欄位 [ManyToOne(0, Name = "Parent", ClassType = typeof(Foo))] [Column(1, Name = "ParentId")] [Column(2, Name = "ParentGroupNumber")] public virtual Foo Parent { get; set; }
說明:
1.由於聯合外鍵與聯合主鍵共用了一個欄位,導致映射出錯
四、終於實現了,總結
- 類都必須可以序列化,也就是要還serializable標註
- 繼承BaseInfo實現聯合主鍵(不推薦使用)
在Save時,如果用session.merge方法組合緩存與修改對象,返回值的主鍵會為Null
- 聯合主鍵與聯合外鍵欄位不能重覆,也不能共用
- 註意重載的GetHashCode和Equals方法
- GetHashCode返回實例的惟一標識
- Equals判斷是否相同實例的具體實現