[NHibernate]一對多關系(級聯刪除,級聯添加)


目錄

寫在前面

文檔與系列文章

一對多關系

一個例子

級聯刪除

級聯保存

總結

寫在前面

在前面的文章中,我們只使用了一個Customer類進行舉例,而在客戶、訂單、產品中它們的關系,咱們並沒有涉及,比如一個客戶可以有一個或者多個訂單,在數據庫中變現為“主外鍵關系”,有時也喜歡稱為“父子關系”。那么就讓我們一起學習,在nhibernate中,是如何處理這種關系的吧?

文檔與系列文章

[Nhibernate]體系結構

[NHibernate]ISessionFactory配置

[NHibernate]持久化類(Persistent Classes)

[NHibernate]O/R Mapping基礎

[NHibernate]集合類(Collections)映射 

[NHibernate]關聯映射

[NHibernate]Parent/Child

[NHibernate]緩存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置與測試 

[NHibernate]HQL查詢 

[NHibernate]條件查詢Criteria Query

[NHibernate]增刪改操作

[NHibernate]事務

[NHibernate]並發控制

[NHibernate]組件之依賴對象

一對多關系

首先看一下數據表的關系。

這里先使用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>
View Code
  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 }
View Code

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 }
View Code

添加訂單生成的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     }
View Code

不然有可能出現下面的異常
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"的情況,因為此時更符合咱們的思維方式,效率上也更好。

參考文章

http://www.cnblogs.com/lyj/archive/2008/10/24/1319052.html

http://home.cnblogs.com/group/topic/39307.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM