天天看點

NHibernate中關于Inverse的了解和使用

在項目中NHibernate進行ORMapping,操作資料庫變得非常簡單,但是NHibernate中有很多特性不是很容易了解,比如Inverse這個功能就是其中的一個。

在使用NHibernate進行資料庫操作的時候,比如資料插入的時候,經常用到級聯功能,比如最常見的就是一個訂單對應多個明細行,在儲存訂單時隻需要Save訂單對象即可,訂單下的所有明細行會級聯儲存。在對象模型層面,Order對象中有個屬性IList<OrderItem> Items,對應其中的訂單明細OrderItem。對于OrderItem對象,其中可以沒有Order對象的引用,如果有Order對象的引用,那麼就是雙向關聯Bidirectional!

對于Bidirectional的情況,那麼在儲存資料到資料庫時就會涉及到一個問題,如果兩邊的資料不一緻,也就是mismatch,到底是以Order中的Items為準還是以OrderItem中的Order為準?NHibernate Cookbook中是這樣說的:

To work around this mismatch, NHibernate ignores one side of the bidirectional relationship. The foreign key in the database is populated based on either the OrderItems reference to the  Order or the  Orders collection of  OrderItems, but not both. We determine which end of the relationship controls the foreign key using the inverse attribute on the collection. By default, the Order controls the foreign key. Saving a new  Order with one OrderItem will result in the following three SQL statements:

When we specify inverse="true", the OrderItem controls the foreign key. This is preferable because it eliminates the extra  UPDATE statement, resulting in the following   two SQL statements:

大體意思就是,NHibernate預設使用Order的屬性作為有效的關聯,換句話說,隻需要把OrderItem一個個的加入到Order的Items集合即可,最終結果不需要關心OrderItem中引用的Order到底是什麼或者為空。如果在Mapping配置Order的Item時設定inverse="true",那麼NHibernate就會使用OrderItem的Order引用作為關聯。

SQL語句上可以看到明細的差別,在預設Inverse為false的情況下,在儲存OrderItem時,其資料庫的字段OrderId是設為null,然後再将Order的Id重新Update到OrderItem中。

【注意:這裡是說最終結果,而不是中間結果,在Insert OrderItem的時候,其OrderId為該對象對應的Order對象的Id,如果該Order對象未儲存,則OrderId為null,如果是已儲存的,則是該Order的Id,然後接下來會更新該OrderId。】

接下來舉一個具體的例子,部門和員工,一對多關系,部門D1,D2,員工U1和U2,D1的Users裡面有U1和U2,U1對象引用D1,U2對象引用D2。

預設不設定Inverse的情況下如果先儲存d1,後儲存d2,會生成如下的SQL:

仔細分析這些SQL語句,就會發現在insert儲存U1時,其DepartmentId是有值的,而Insert儲存U2時,其DepartmentId是null,這是因為D2現在還沒有儲存到資料庫,沒有Id,是以插入Null,接下來是儲存D2,在儲存了D2後有了Id,那麼就需要更新U2的DepartmentId,讓其等于D2的Id。以上都是插入過程,接下來還要進行外鍵更新操作,保證資料庫中的外鍵與對象中Department中設定的Users保持一緻,是以Update每個User表即可。

如果是改為Inverse=True,那麼然後儲存d1和d2,那麼對應的SQL是:

可以看出,最大的不同是沒有了最後兩句更新外鍵的SQL。如果我們再調整下儲存的順序,先儲存D2,然後再儲存D1,那麼對應的SQL是:

顯然第一種SQL語句進行了外鍵的update操作,沒有第二三次的效率高,而且,必須要設定資料庫中OrderItem的OrderId允許為空。從資料庫模型來說,這個不合理啊!

是以一般建議在Mapping時設定Inverse為True。對應的,在Code中也需要設定OrderItem對Order的引用。

Inverse更大的用處是在ManyToMany的時候。如果兩邊Inverse=False的情況下,ManyToMany是任意一邊設定集合并儲存就有效,如果兩邊都設定的話,會儲存多次。比如有員工E1和E2,獎品A1和A2,其是多對多關系,如果要設定E1員工獲得A1和A2獎,那麼需要設定各自的集合:

從DomainModel來說,這樣設定是對的,但是生成SQL卻有問題:

明明應該是往中間表插入2條記錄的,但是這樣5-8行卻變成了插入4條記錄。如果中間表設定了聯合主鍵,那麼必然會報錯,插入失敗。

這個時候可以在Award端設定Inverse=True,Emp端設定Inverse=False,表示其多對多關系不在Award方維護,隻在Emp端維護:

這樣設定了Mapping後,就可以生成正确的SQL語句,當然如果把C#代碼中的6行和7行去掉,結果也是正确的,因為現在系統隻認Emp中的Awards集合了。但是如果删除第5行,保留6-7行則不行。

總結:

Inverse用于設定雙向關聯時Nhibernate在設定外鍵時依賴的對象,預設Inverse=False,一對多時表示依賴一端的集合,如果為True表示依賴多段對象中對一端對象的引用。

多對多時不能讓兩端的Inverse為False,這樣會造成資料的重複插入;必須設定一端為False,一端為True。

本文轉自深藍居部落格園部落格,原文連結:http://www.cnblogs.com/studyzy/archive/2012/07/23/2605325.html,如需轉載請自行聯系原作者