目錄
寫在前面
在前面的文章中,我們只使用了一個Customer類進行舉例,而在客戶、訂單、產品中它們的關系,咱們並沒有涉及,比如一個客戶可以有一個或者多個訂單,在數據庫中變現為“主外鍵關系”,有時也喜歡稱為“父子關系”。那么就讓我們一起學習,在nhibernate中,是如何處理這種關系的吧?
文檔與系列文章
[NHibernate]持久化類(Persistent Classes)
[NHibernate]集合類(Collections)映射
[NHibernate]緩存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]條件查詢Criteria Query
一對多關系
首先看一下數據表的關系。

這里先使用Customer和Order的關系為例進行說明,一個客戶可以有一個或者多個訂單的關系。
在nhibernate中定義了多種集合方式:
Bag:對象集合,集合中的元素可以重復。例如:{1,2,3,4,2,3},相當於.Net中的IList和IList<T>。
Set:對象集合,集合中的元素必須唯一。例如:{1,3,4},相當於.Net中的ISet和ISet<T>,nhibernate的Iesi.Collections.dll程序集提供ISet集合。
List:整數索引集合,集合中的元素可以重復。例如:{{1,"zhangsan"},{2,"lisi"}},相當於.Net中ArrayList和List集合。
Map:鍵值對集合,相當於.Net中的IDictionary<TKey,TValue>和Hashtable。
使用過Entity Framework的朋友都知道,在EF中使用的ISet,為了保持一直,這里也使用ISet集合,也方便以后如果使用ef的時候,用起來更順手。
一個例子
在nhibernate-4.0版本中,在命名空間Iesi.Collections.Generic中已經沒有ISet集合了,那么我們使用.net中的ISet代替來描述這種一對多的關系,Customer.cs類代碼如下:
1 /// <summary> 2 /// 描述:客戶實體,數據庫持久化類 3 /// 創建人:wolfy 4 /// 創建時間:2014-10-16 5 /// </summary> 6 public class Customer 7 { 8 /// <summary> 9 /// 客戶id 10 /// </summary> 11 public virtual Guid CustomerID { get; set; } 12 /// <summary> 13 /// 客戶名字 14 /// </summary> 15 public virtual Name NameAddress { get; set; } 16 /// <summary> 17 /// 版本控制 18 /// </summary> 19 public virtual int Version { get; set; } 20 /// <summary> 21 /// 一對多關系:一個Customer有一個或者多個Order 22 /// </summary> 23 public virtual System.Collections.Generic.ISet<Order> Orders { set; get; } 24 }
修改Order類
1 /// <summary> 2 /// 描述:訂單實體,數據庫持久化類 3 /// 創建人:wolfy 4 /// 創建時間:2014-10-16 5 /// </summary> 6 public class Order 7 { 8 /// <summary> 9 /// 訂單id 10 /// </summary> 11 public virtual Guid OrderID { set; get; } 12 /// <summary> 13 /// 下訂單時間 14 /// </summary> 15 public virtual DateTime OrderDate { set; get; } 16 /// <summary> 17 /// 下訂單的客戶,多對一的關系:orders對應一個客戶 18 /// </summary> 19 public virtual Customer Customer { set; get; } 20 }
在nhibernate中,可以通過映射文件來關聯對象之間的關系。
- 對象之間的關系:一對一,多對一,一對多,多對多關系。
- 在關系中控制級聯行為(Cascade behavior):級聯刪除,級聯更新。
- 父子關系的雙向導(bidirectional navigation)
父實體映射
父實體Customer映射文件中,定義了:
集合類型:Set,Bag,List,Map
在保存,更新,刪除操作時的級聯行為。
關聯的控制方向:
inverse=“false”(默認)父實體負責維護關聯關系。
inverse=“true”子實體負責維護關聯關系。
與子實體的關系:一對多,多對一,多對多。
修改Customer.hbm.xml映射文件,描述一個客戶對應多個訂單的關系
1 <?xml version="1.0" encoding="utf-8" ?> 2 <!--assembly:程序集,namespace:命名空間--> 3 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities"> 4 <class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer"> 5 <!--主鍵--> 6 <id name="CustomerID" type="Guid" unsaved-value="null"> 7 <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true"/> 8 <generator class="assigned"></generator> 9 </id> 10 <!--版本控制--> 11 <version name="Version" column="Version" type="integer" unsaved-value="0"/> 12 <!--組件 name組件屬性名--> 13 <component name="NameAddress" class="Wolfy.Shop.Domain.Entities.Name,Wolfy.Shop.Domain"> 14 <!--Name類中的屬性property--> 15 <property name="CustomerName" column ="CustomerName" type="string" 16 length="16" not-null="false" /> 17 <property name ="CustomerAddress" column="CustomerAddress" type="string" 18 length="128" not-null="false" /> 19 </component> 20 <!--一對多關系:一個客戶可以有一個或者多個訂單--> 21 <!--子實體負責維護關聯關系--> 22 <set name="Orders" table="TB_Order" generic="true" inverse="true"> 23 <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key> 24 <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/> 25 </set> 26 </class> 27 </hibernate-mapping>
添加Order.hbm.xml映射文件
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities"> 3 <class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order"> 4 <id name="OrderID" column="OrderID" type="Guid" unsaved-value="null"> 5 <generator class="assigned" /> 6 </id> 7 <property name="OrderDate" column="OrderDate" type="DateTime" 8 not-null="true" /> 9 <!--多對一關系:Orders屬於一個Customer--> 10 <many-to-one name="Customer" column="CustomerID" not-null="true" 11 class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" 12 foreign-key="FK_TB_Order_TB_Customer" /> 13 </class> 14 </hibernate-mapping>
many-to-one節點屬性介紹:
子實體(Order)映射定義:與父實體關聯的(多對一、一對多、多對多) 關系,並用一個指針來導航到父實體。
在“子”端通過many-to-one元素定義與“父”端的關聯,從“子”端角度看這種關系模型是多對一關聯(實際上是對Customer對象的引用)。下面看看many-to-one元素映射屬性:

access(默認property):可選field、property、nosetter、ClassName值。NHibernate訪問屬性的策略。
cascade(可選):指明哪些操作會從父對象級聯到關聯的對象。可選all、save-update、delete、none值。除none之外其它將使指定的操作延伸到關聯的(子)對象。
class(默認通過反射得到屬性類型):關聯類的名字。
column(默認屬性名):列名。
fetch(默認select):可選select和join值,select:用單獨的查詢抓取關聯;join:總是用外連接抓取關聯。
foreign-key:外鍵名稱,使用SchemaExport工具生成的名稱。
index:......
update,insert(默認true):指定對應的字段是否包含在用於UPDATE或INSERT 的SQL語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關聯,它的值是通過映射到同一個(或多個)字段的某些其他特性得到或者通過觸發器其他程序得到。
lazy:可選false和proxy值。是否延遲,不延遲還是使用代理延遲。
name:屬性名稱propertyName。
not-found:可選ignore和exception值。找不到忽略或者拋出異常。
not-null:可選true和false值。
outer-join:可選auto、true、false值。
property-ref(可選):指定關聯類的一個屬性名稱,這個屬性會和外鍵相對應。如果沒有指定,會使用對方關聯類的主鍵。這個屬性通常在遺留的數據庫系統使用,可能有外鍵指向對方關聯表的某個非主鍵字段(但是應該是一個唯一關鍵字)的情況下,是非常不好的關系模型。比如說,假設Customer類有唯一的CustomerId,它並不是主鍵。這一點在NHibernate源碼中有了充分的體驗。
unique:可選true和false值。控制NHibernate通過SchemaExport工具生成DDL的過程。
unique-key(可選):使用DDL為外鍵字段生成一個唯一約束。
編寫OrderData.cs代碼,添加Order。
1 /// <summary> 2 /// 描述:訂單數據層 3 /// 創建人:wolfy 4 /// 創建時間:2014-11-02 5 /// </summary> 6 public class OrderData 7 { 8 /// <summary> 9 /// 添加訂單 10 /// </summary> 11 /// <param name="order"></param> 12 /// <returns></returns> 13 public bool AddOrder(Order order) 14 { 15 try 16 { 17 NHibernateHelper nhibernateHelper = new NHibernateHelper(); 18 var session = nhibernateHelper.GetSession(); 19 session.SaveOrUpdate(order); 20 session.Flush(); 21 return true; 22 } 23 catch (Exception) 24 { 25 throw; 26 } 27 } 28 }
測試頁面AddCustomer.aspx
1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CustomerManager.aspx.cs" Inherits="Wolfy.Shop.WebSite.CustomerManager" %> 2 3 <!DOCTYPE html> 4 5 <html xmlns="http://www.w3.org/1999/xhtml"> 6 <head runat="server"> 7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 8 <title></title> 9 <style type="text/css"> 10 .main { 11 border: 1px solid #0094ff; 12 margin: 50px auto; 13 width: 600px; 14 } 15 16 .table { 17 border: 1px solid #0094ff; 18 border-collapse: collapse; 19 width: 98%; 20 text-align: center; 21 margin: 5px auto; 22 } 23 24 .table th { 25 background-color: lightgray; 26 } 27 28 .table tr td { 29 border: 1px solid #0094ff; 30 } 31 </style> 32 </head> 33 <body> 34 <form id="form1" runat="server"> 35 <div class="main"> 36 <asp:Button runat="server" ID="btnAdd" Text="添加" OnClick="btnAdd_Click" /><asp:Button Text="並發更新" ID="btnSameTimeUpdate" runat="server" OnClick="btnSameTimeUpdate_Click" /><br /> 37 按姓名查詢: 38 <asp:TextBox runat="server" ID="txtName" /> 39 <asp:TextBox runat="server" ID="txtAddress" /> 40 <asp:Button Text="查詢" runat="server" ID="btnSearch" OnClick="btnSearch_Click" /> 41 42 <div> 43 <asp:Repeater runat="server" ID="rptCustomerList"> 44 <HeaderTemplate> 45 <table class="table"> 46 <tr> 47 <th>姓名</th> 48 <th>姓名</th> 49 <th>地址</th> 50 <th>版本</th> 51 <th>姓名/地址</th> 52 <th>操作</th> 53 </tr> 54 </HeaderTemplate> 55 <ItemTemplate> 56 <tr> 57 <td><%#Container.ItemIndex+1 %></td> 58 <td><%#Eval("NameAddress.CustomerName") %></td> 59 <td><%#Eval("NameAddress.CustomerAddress") %></td> 60 <td><%#Eval("Version") %></td> 61 <td><%#Eval("NameAddress.NameAddress") %></td> 62 <td> 63 <asp:LinkButton runat="server" ID="lnkOrder" CommandArgument='<%#Eval("CustomerID") %>' CommandName="Order" OnClick="lnkOrder_Click">下單</asp:LinkButton></td> 64 </tr> 65 </ItemTemplate> 66 <FooterTemplate> 67 </table> 68 </FooterTemplate> 69 </asp:Repeater> 70 </div> 71 </div> 72 </form> 73 </body> 74 </html>
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 7 using Wolfy.Shop.Domain.Entities; 8 9 namespace Wolfy.Shop.WebSite 10 { 11 public partial class CustomerManager : System.Web.UI.Page 12 { 13 protected void Page_Load(object sender, EventArgs e) 14 { 15 if (!IsPostBack) 16 { 17 RepeaterDataBind(); 18 //List<Guid> lst = new Business.CustomerBusiness().GetAllCustomerID() as List<Guid>; 19 //foreach (var item in lst) 20 //{ 21 // Response.Write(item.ToString()); 22 //} 23 // Response.Write(new Business.CustomerBusiness().GetCustomerByName("wolfy").FirstOrDefault().CustomerName); 24 } 25 } 26 private void RepeaterDataBind() 27 { 28 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness(); 29 this.rptCustomerList.DataSource = customerBusiness.GetCustomerList(c => 1 == 1); //customerBusiness.GetCustomers(); 30 this.rptCustomerList.DataBind(); 31 } 32 /// <summary> 33 /// 添加客戶信息 34 /// </summary> 35 /// <param name="sender"></param> 36 /// <param name="e"></param> 37 protected void btnAdd_Click(object sender, EventArgs e) 38 { 39 Guid guidCustomerID = Guid.NewGuid(); 40 var customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, CustomerID = guidCustomerID }; 41 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness(); 42 //使用事務的方式添加客戶信息 43 if (customerBusiness.SaveOrUpdateByTrans(customer)) 44 { 45 RepeaterDataBind(); 46 } 47 //提供一個名字長度溢出的測試數據 48 customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, CustomerID = Guid.NewGuid() }; 49 //使用事務的方式添加客戶信息 50 if (customerBusiness.SaveOrUpdateByTrans(customer)) 51 { 52 RepeaterDataBind(); 53 } 54 } 55 /// <summary> 56 /// 查詢 57 /// </summary> 58 /// <param name="sender"></param> 59 /// <param name="e"></param> 60 protected void btnSearch_Click(object sender, EventArgs e) 61 { 62 63 this.rptCustomerList.DataSource = new Business.CustomerBusiness().GetCustomerAddress(this.txtName.Text, this.txtAddress.Text); 64 this.rptCustomerList.DataBind(); 65 } 66 /// <summary> 67 /// 並發更新操作 68 /// </summary> 69 /// <param name="sender"></param> 70 /// <param name="e"></param> 71 protected void btnSameTimeUpdate_Click(object sender, EventArgs e) 72 { 73 Guid guidCustomerId = new Guid("82724514-682E-4E6F-B759-02E499CDA50F"); 74 //模擬第一個修改數據 75 Customer c1 = new Customer() 76 { 77 CustomerID = guidCustomerId, 78 NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, 79 Version = 1 80 }; 81 //模擬第二個修改數據 82 Customer c2 = new Customer() 83 { 84 CustomerID = guidCustomerId, 85 NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, 86 Version = 1 87 }; 88 89 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness(); 90 customerBusiness.SaveOrUpdateByTrans(c1); 91 customerBusiness.SaveOrUpdateByTrans(c2); 92 } 93 /// <summary> 94 /// 下訂單 95 /// </summary> 96 /// <param name="sender"></param> 97 /// <param name="e"></param> 98 protected void lnkOrder_Click(object sender, EventArgs e) 99 { 100 LinkButton lnkBtn = sender as LinkButton; 101 if (lnkBtn!=null) 102 { 103 Response.Redirect("AddOrder.aspx?cid="+lnkBtn.CommandArgument); 104 } 105 } 106 } 107 }
AddOrder.aspx.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 7 using Wolfy.Shop.Domain.Entities; 8 9 namespace Wolfy.Shop.WebSite 10 { 11 public partial class AddOrder : System.Web.UI.Page 12 { 13 protected void Page_Load(object sender, EventArgs e) 14 { 15 if (!IsPostBack) 16 { 17 string strCid = Request.QueryString["cid"]; 18 if (!string.IsNullOrEmpty(strCid)) 19 { 20 Business.OrderBusiness orderBusiness = new Business.OrderBusiness(); 21 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness(); 22 Order order = new Order() { Customer = customerBusiness.GetCustomerList(c => c.CustomerID == new Guid(strCid)).FirstOrDefault(), OrderDate = DateTime.Now, OrderID = Guid.NewGuid() }; 23 if (orderBusiness.AddOrder(order)) 24 { 25 Response.Write("添加成功"); 26 } 27 } 28 } 29 } 30 } 31 }
添加訂單生成的sql
使用單例模式修改NHibernateHelper
1 /// <summary> 2 /// 描述:nhibernate輔助類 3 /// 創建人:wolfy 4 /// 創建時間:2014-10-16 5 /// </summary> 6 public class NHibernateHelper 7 { 8 private static ISessionFactory _sessionFactory; 9 private static ISession _session; 10 private static object _objLock = new object(); 11 private NHibernateHelper() 12 { 13 14 } 15 /// <summary> 16 /// 創建ISessionFactory 17 /// </summary> 18 /// <returns></returns> 19 public static ISessionFactory GetSessionFactory() 20 { 21 if (_sessionFactory == null) 22 { 23 lock (_objLock) 24 { 25 if (_sessionFactory == null) 26 { 27 //配置ISessionFactory 28 _sessionFactory = (new Configuration()).Configure().BuildSessionFactory(); 29 } 30 } 31 } 32 return _sessionFactory; 33 34 } 35 /// <summary> 36 /// 打開ISession 37 /// </summary> 38 /// <returns></returns> 39 public static ISession GetSession() 40 { 41 _sessionFactory = GetSessionFactory(); 42 if (_session == null) 43 { 44 lock (_objLock) 45 { 46 if (_session == null) 47 { 48 _session = _sessionFactory.OpenSession(); 49 } 50 } 51 } 52 return _session; 53 } 54 }
不然有可能出現下面的異常
Illegal attempt to associate a collection with two open sessions
級聯刪除
在級聯刪除過程中遇到的問題
1,設置Customer.hbm.xml中set屬性inverse=“true”,必須設置cascade屬性。
1 <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all" > 2 <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key> 3 <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/> 4 </set>
此時添加訂單,生成的數據如下圖所示

當刪除CustomerID=‘3042B445-D3AC-4CC0-BDFF-311982E4265A’的客戶信息時。
生成的sql語句
1 exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='574BFFF6-D4EC-4EFA-9DBD-32817CD922EC' 2 exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='355FF069-FD95-4F33-A6DF-D1AD90CB56D0' 3 exec sp_executesql N'DELETE FROM TB_Customer WHERE CustomerID = @p0 AND Version = @p1',N'@p0 uniqueidentifier,@p1 int',@p0='3042B445-D3AC-4CC0-BDFF-311982E4265A',@p1=1
如果不設置cascade屬性,會產生如下異常

因為inverse=“true”子實體負責維護關聯關系。此時你刪除Customer中的記錄時,由於在Order表中有對應於該客戶的訂單信息,會違反外鍵約束。
cascade(可選):指明哪些操作會從父對象級聯到關聯的對象。可選all、save-update、delete、none值。除none之外其它將使指定的操作延伸到關聯的(子)對象。
2、設置Customer.hbm.xml中set屬性inverse=“false”時。
inverse=“false”(默認)父實體負責維護關聯關系。
此時如果刪除Customer,則屬於該客戶的訂單,會將訂單表中的Customerid置為null。

刪除CustomerID=‘B0387B7E-C9F3-4970-9429-955DDDC2ACAA’

看一下生成的sql語句
exec sp_executesql N'UPDATE TB_Order SET CustomerID = null WHERE CustomerID = @p0',N'@p0 uniqueidentifier',@p0='B0387B7E-C9F3-4970-9429-955DDDC2ACAA' exec sp_executesql N'DELETE FROM TB_Customer WHERE CustomerID = @p0 AND Version = @p1',N'@p0 uniqueidentifier,@p1 int',@p0='B0387B7E-C9F3-4970-9429-955DDDC2ACAA',@p1=1
此時通過sql語句也可以看到,如果是父實體負責維護關聯關系,會在子表中將外鍵置為null,如果你的外鍵不允許為null,肯定會有異常的。
如果加上cascade="all"屬性

通過生成的sql語句,你會發現此時仍會修改外鍵為null,然后再去刪除
1 exec sp_executesql N'UPDATE TB_Order SET CustomerID = null WHERE CustomerID = @p0',N'@p0 uniqueidentifier',@p0='82724514-682E-4E6F-B759-02E499CDA50F' 2 exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='AD7EB390-70BC-4ECF-B593-15A24A40CD26' 3 exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='7D21E3FC-CDCF-4586-9714-D6B023CCBFA9' 4 exec sp_executesql N'DELETE FROM TB_Customer WHERE CustomerID = @p0 AND Version = @p1',N'@p0 uniqueidentifier,@p1 int',@p0='82724514-682E-4E6F-B759-02E499CDA50F',@p1=2
如果在注重效率的情況下,這中方式沒有采用子實體維護關聯關系的方式效率高。
通過上面的分析,不管是父實體維護關聯關系還是子實體維護關聯關系,為映射set節點加上cascade屬性是必須的。
級聯保存
一個簡單的例子,在添加客戶的時候,直接為用戶添加訂單。
1 /// <summary> 2 /// 添加客戶和訂單 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 protected void btnCsOrder_Click(object sender, EventArgs e) 7 { 8 var customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsanOrder", CustomerAddress = "北京 海淀" }, CustomerID = Guid.NewGuid() }; 9 customer.Orders = new HashSet<Order>(); 10 customer.Orders.Add(new Order() { OrderID = Guid.NewGuid(), OrderDate = DateTime.Now, Customer = customer }); 11 customer.Orders.Add(new Order() { OrderID = Guid.NewGuid(), OrderDate = DateTime.Now, Customer = customer }); 12 Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness(); 13 if (customerBusiness.AddCustomer(customer)) 14 { 15 this.RepeaterDataBind(); 16 } 17 }
此時的Customer.hbm.xml映射中Set節點的配置如下
<!--一對多關系:一個客戶可以有一個或者多個訂單--> <!--子實體負責維護關聯關系--> <set name="Orders" table="TB_Order" generic="true" inverse="false" cascade="all"> <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key> <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/> </set>
生成的sql語句為

你會發現,添加的一個客戶信息,對應添加兩個Order,添加Order后會執行Update
exec sp_executesql N'INSERT INTO TB_Customer (Version, CustomerName, CustomerAddress, CustomerID) VALUES (@p0, @p1, @p2, @p3)',N'@p0 int,@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 uniqueidentifier',@p0=1,@p1=N'zhangsanOrder',@p2=N'北京 海淀',@p3='4E0C307A-E221-41D5-849D-C6E9F271BD36' exec sp_executesql N'INSERT INTO TB_Order (OrderDate, CustomerID, OrderID) VALUES (@p0, @p1, @p2)',N'@p0 datetime,@p1 uniqueidentifier,@p2 uniqueidentifier',@p0='2014-11-02 14:24:42',@p1='4E0C307A-E221-41D5-849D-C6E9F271BD36',@p2='E44DF3B5-BFD1-4C09-96B5-492D7CAEAADA' exec sp_executesql N'INSERT INTO TB_Order (OrderDate, CustomerID, OrderID) VALUES (@p0, @p1, @p2)',N'@p0 datetime,@p1 uniqueidentifier,@p2 uniqueidentifier',@p0='2014-11-02 14:24:42',@p1='4E0C307A-E221-41D5-849D-C6E9F271BD36',@p2='4E5524D3-48A9-4501-8A52-CD181B843608' exec sp_executesql N'UPDATE TB_Order SET CustomerID = @p0 WHERE OrderID = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='4E0C307A-E221-41D5-849D-C6E9F271BD36',@p1='E44DF3B5-BFD1-4C09-96B5-492D7CAEAADA' exec sp_executesql N'UPDATE TB_Order SET CustomerID = @p0 WHERE OrderID = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='4E0C307A-E221-41D5-849D-C6E9F271BD36',@p1='4E5524D3-48A9-4501-8A52-CD181B843608'
這確實有點奇怪了,難道是為了在插入后通過修改外鍵,來保證這種外鍵約束嗎?
為了一探究竟,對比一下,inverse=true的情況
1 <!--一對多關系:一個客戶可以有一個或者多個訂單--> 2 <!--子實體負責維護關聯關系--> 3 <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all"> 4 <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key> 5 <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/> 6 </set>
此時生成的sql

1 exec sp_executesql N'INSERT INTO TB_Customer (Version, CustomerName, CustomerAddress, CustomerID) VALUES (@p0, @p1, @p2, @p3)',N'@p0 int,@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 uniqueidentifier',@p0=1,@p1=N'zhangsanOrder',@p2=N'北京 海淀',@p3='B0720295-9541-40B3-9994-610066224DB8' 2 exec sp_executesql N'INSERT INTO TB_Order (OrderDate, CustomerID, OrderID) VALUES (@p0, @p1, @p2)',N'@p0 datetime,@p1 uniqueidentifier,@p2 uniqueidentifier',@p0='2014-11-02 14:33:09',@p1='B0720295-9541-40B3-9994-610066224DB8',@p2='78A53F67-A293-48A1-BBE2-86FED77342FA' 3 exec sp_executesql N'INSERT INTO TB_Order (OrderDate, CustomerID, OrderID) VALUES (@p0, @p1, @p2)',N'@p0 datetime,@p1 uniqueidentifier,@p2 uniqueidentifier',@p0='2014-11-02 14:33:09',@p1='B0720295-9541-40B3-9994-610066224DB8',@p2='51CA6A37-EC79-4613-B8D7-CEEF5A4BB8EE'
可以通過子實體來維護級聯關系,更符合程序員的邏輯習慣,並且效率上更好一些。
總結
a)
inverse="false"
cascade="all"
結果:Customer和Order都刪了
b)
inverse="true"
cascade="all"
結果:Customer和Order都刪了
c)
不設置inverse
不設置cascade
結果:Customer刪除了,Order的Customer設為了空
d)
不設置inverse
設置cascade ="delete"
結果:Customer和Order都刪了
e)
不設置inverse
設置cascade ="save-update"
結果:Customer刪除了,Order的Customer設為了空
f)
不設置inverse
設置cascade ="all-delete-orphan"
結果:Customer和Order都刪了
通過上面的級聯刪除與添加分析,建議使用inverse="true" cascade="all"的情況,因為此時更符合咱們的思維方式,效率上也更好。
參考文章
