[NHibernate]一对多关系(级联删除,级联添加)
目录
写在前面
在前面的文章中,我们只使用了一个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类代码如下:
/// <summary>
/// 描述:客户实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Customer
{
/// <summary>
/// 客户id
/// </summary>
public virtual Guid CustomerID { get; set; }
/// <summary>
/// 客户名字
/// </summary>
public virtual Name NameAddress { get; set; }
/// <summary>
/// 版本控制
/// </summary>
public virtual int Version { get; set; }
/// <summary>
/// 一对多关系:一个Customer有一个或者多个Order
/// </summary>
public virtual System.Collections.Generic.ISet<Order> Orders { set; get; }
}
修改Order类
/// <summary>
/// 描述:订单实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Order
{
/// <summary>
/// 订单id
/// </summary>
public virtual Guid OrderID { set; get; }
/// <summary>
/// 下订单时间
/// </summary>
public virtual DateTime OrderDate { set; get; }
/// <summary>
/// 下订单的客户,多对一的关系:orders对应一个客户
/// </summary>
public virtual Customer Customer { set; get; }
}
在nhibernate中,可以通过映射文件来关联对象之间的关系。
- 对象之间的关系:一对一,多对一,一对多,多对多关系。
- 在关系中控制级联行为(Cascade behavior):级联删除,级联更新。
- 父子关系的双向导(bidirectional navigation)
父实体映射
父实体Customer映射文件中,定义了:
集合类型:Set,Bag,List,Map
在保存,更新,删除操作时的级联行为。
关联的控制方向:
inverse=“false”(默认)父实体负责维护关联关系。
inverse=“true”子实体负责维护关联关系。
与子实体的关系:一对多,多对一,多对多。
修改Customer.hbm.xml映射文件,描述一个客户对应多个订单的关系
<?xml version="1.0" encoding="utf-8" ?>
<!--assembly:程序集,namespace:命名空间-->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
<!--主键-->
<id name="CustomerID" type="Guid" unsaved-value="null">
<column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true"/>
<generator class="assigned"></generator>
</id>
<!--版本控制-->
<version name="Version" column="Version" type="integer" unsaved-value="0"/>
<!--组件 name组件属性名-->
<component name="NameAddress" class="Wolfy.Shop.Domain.Entities.Name,Wolfy.Shop.Domain">
<!--Name类中的属性property-->
<property name="CustomerName" column ="CustomerName" type="string"
length="16" not-null="false" />
<property name ="CustomerAddress" column="CustomerAddress" type="string"
length="128" not-null="false" />
</component>
<!--一对多关系:一个客户可以有一个或者多个订单-->
<!--子实体负责维护关联关系-->
<set name="Orders" table="TB_Order" generic="true" inverse="true">
<key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key>
<one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/>
</set>
</class>
</hibernate-mapping>
添加Order.hbm.xml映射文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain" table="TB_Order">
<id name="OrderID" column="OrderID" type="Guid" unsaved-value="null">
<generator class="assigned" />
</id>
<property name="OrderDate" column="OrderDate" type="DateTime"
not-null="true" />
<!--多对一关系:Orders属于一个Customer-->
<many-to-one name="Customer" column="CustomerID" not-null="true"
class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain"
foreign-key="FK_TB_Order_TB_Customer" />
</class>
</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。
/// <summary>
/// 描述:订单数据层
/// 创建人:wolfy
/// 创建时间:2014-11-02
/// </summary>
public class OrderData
{
/// <summary>
/// 添加订单
/// </summary>
/// <param name="order"></param>
/// <returns></returns>
public bool AddOrder(Order order)
{
try
{
NHibernateHelper nhibernateHelper = new NHibernateHelper();
var session = nhibernateHelper.GetSession();
session.SaveOrUpdate(order);
session.Flush();
return true;
}
catch (Exception)
{
throw;
}
}
}
测试页面AddCustomer.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CustomerManager.aspx.cs" Inherits="Wolfy.Shop.WebSite.CustomerManager" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<style type="text/css">
.main {
border: 1px solid #0094ff;
margin: 50px auto;
width: 600px;
} .table {
border: 1px solid #0094ff;
border-collapse: collapse;
width: 98%;
text-align: center;
margin: 5px auto;
} .table th {
background-color: lightgray;
} .table tr td {
border: 1px solid #0094ff;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div class="main">
<asp:Button runat="server" ID="btnAdd" Text="添加" OnClick="btnAdd_Click" /><asp:Button Text="并发更新" ID="btnSameTimeUpdate" runat="server" OnClick="btnSameTimeUpdate_Click" /><br />
按姓名查询:
<asp:TextBox runat="server" ID="txtName" />
<asp:TextBox runat="server" ID="txtAddress" />
<asp:Button Text="查询" runat="server" ID="btnSearch" OnClick="btnSearch_Click" /> <div>
<asp:Repeater runat="server" ID="rptCustomerList">
<HeaderTemplate>
<table class="table">
<tr>
<th>姓名</th>
<th>姓名</th>
<th>地址</th>
<th>版本</th>
<th>姓名/地址</th>
<th>操作</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><%#Container.ItemIndex+1 %></td>
<td><%#Eval("NameAddress.CustomerName") %></td>
<td><%#Eval("NameAddress.CustomerAddress") %></td>
<td><%#Eval("Version") %></td>
<td><%#Eval("NameAddress.NameAddress") %></td>
<td>
<asp:LinkButton runat="server" ID="lnkOrder" CommandArgument='<%#Eval("CustomerID") %>' CommandName="Order" OnClick="lnkOrder_Click">下单</asp:LinkButton></td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</div>
</form>
</body>
</html>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Wolfy.Shop.Domain.Entities; namespace Wolfy.Shop.WebSite
{
public partial class CustomerManager : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
RepeaterDataBind();
//List<Guid> lst = new Business.CustomerBusiness().GetAllCustomerID() as List<Guid>;
//foreach (var item in lst)
//{
// Response.Write(item.ToString());
//}
// Response.Write(new Business.CustomerBusiness().GetCustomerByName("wolfy").FirstOrDefault().CustomerName);
}
}
private void RepeaterDataBind()
{
Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
this.rptCustomerList.DataSource = customerBusiness.GetCustomerList(c => == ); //customerBusiness.GetCustomers();
this.rptCustomerList.DataBind();
}
/// <summary>
/// 添加客户信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnAdd_Click(object sender, EventArgs e)
{
Guid guidCustomerID = Guid.NewGuid();
var customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, CustomerID = guidCustomerID };
Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
//使用事务的方式添加客户信息
if (customerBusiness.SaveOrUpdateByTrans(customer))
{
RepeaterDataBind();
}
//提供一个名字长度溢出的测试数据
customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" }, CustomerID = Guid.NewGuid() };
//使用事务的方式添加客户信息
if (customerBusiness.SaveOrUpdateByTrans(customer))
{
RepeaterDataBind();
}
}
/// <summary>
/// 查询
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSearch_Click(object sender, EventArgs e)
{ this.rptCustomerList.DataSource = new Business.CustomerBusiness().GetCustomerAddress(this.txtName.Text, this.txtAddress.Text);
this.rptCustomerList.DataBind();
}
/// <summary>
/// 并发更新操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSameTimeUpdate_Click(object sender, EventArgs e)
{
Guid guidCustomerId = new Guid("82724514-682E-4E6F-B759-02E499CDA50F");
//模拟第一个修改数据
Customer c1 = new Customer()
{
CustomerID = guidCustomerId,
NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" },
Version =
};
//模拟第二个修改数据
Customer c2 = new Customer()
{
CustomerID = guidCustomerId,
NameAddress = new Name() { CustomerName = "zhangsan3322", CustomerAddress = "北京 海淀" },
Version =
}; Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
customerBusiness.SaveOrUpdateByTrans(c1);
customerBusiness.SaveOrUpdateByTrans(c2);
}
/// <summary>
/// 下订单
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void lnkOrder_Click(object sender, EventArgs e)
{
LinkButton lnkBtn = sender as LinkButton;
if (lnkBtn!=null)
{
Response.Redirect("AddOrder.aspx?cid="+lnkBtn.CommandArgument);
}
}
}
}
AddOrder.aspx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Wolfy.Shop.Domain.Entities; namespace Wolfy.Shop.WebSite
{
public partial class AddOrder : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string strCid = Request.QueryString["cid"];
if (!string.IsNullOrEmpty(strCid))
{
Business.OrderBusiness orderBusiness = new Business.OrderBusiness();
Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
Order order = new Order() { Customer = customerBusiness.GetCustomerList(c => c.CustomerID == new Guid(strCid)).FirstOrDefault(), OrderDate = DateTime.Now, OrderID = Guid.NewGuid() };
if (orderBusiness.AddOrder(order))
{
Response.Write("添加成功");
}
}
}
}
}
}
添加订单生成的sql
使用单例模式修改NHibernateHelper
/// <summary>
/// 描述:nhibernate辅助类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISession _session;
private static object _objLock = new object();
private NHibernateHelper()
{ }
/// <summary>
/// 创建ISessionFactory
/// </summary>
/// <returns></returns>
public static ISessionFactory GetSessionFactory()
{
if (_sessionFactory == null)
{
lock (_objLock)
{
if (_sessionFactory == null)
{
//配置ISessionFactory
_sessionFactory = (new Configuration()).Configure().BuildSessionFactory();
}
}
}
return _sessionFactory; }
/// <summary>
/// 打开ISession
/// </summary>
/// <returns></returns>
public static ISession GetSession()
{
_sessionFactory = GetSessionFactory();
if (_session == null)
{
lock (_objLock)
{
if (_session == null)
{
_session = _sessionFactory.OpenSession();
}
}
}
return _session;
}
}
不然有可能出现下面的异常
Illegal attempt to associate a collection with two open sessions
级联删除
在级联删除过程中遇到的问题
1,设置Customer.hbm.xml中set属性inverse=“true”,必须设置cascade属性。
<set name="Orders" table="TB_Order" generic="true" inverse="true" 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>
此时添加订单,生成的数据如下图所示
当删除CustomerID=‘3042B445-D3AC-4CC0-BDFF-311982E4265A’的客户信息时。
生成的sql语句
exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='574BFFF6-D4EC-4EFA-9DBD-32817CD922EC'
exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='355FF069-FD95-4F33-A6DF-D1AD90CB56D0'
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,然后再去删除
exec sp_executesql N'UPDATE TB_Order SET CustomerID = null WHERE CustomerID = @p0',N'@p0 uniqueidentifier',@p0='82724514-682E-4E6F-B759-02E499CDA50F'
exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='AD7EB390-70BC-4ECF-B593-15A24A40CD26'
exec sp_executesql N'DELETE FROM TB_Order WHERE OrderID = @p0',N'@p0 uniqueidentifier',@p0='7D21E3FC-CDCF-4586-9714-D6B023CCBFA9'
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属性是必须的。
级联保存
一个简单的例子,在添加客户的时候,直接为用户添加订单。
/// <summary>
/// 添加客户和订单
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnCsOrder_Click(object sender, EventArgs e)
{
var customer = new Customer() { NameAddress = new Name() { CustomerName = "zhangsanOrder", CustomerAddress = "北京 海淀" }, CustomerID = Guid.NewGuid() };
customer.Orders = new HashSet<Order>();
customer.Orders.Add(new Order() { OrderID = Guid.NewGuid(), OrderDate = DateTime.Now, Customer = customer });
customer.Orders.Add(new Order() { OrderID = Guid.NewGuid(), OrderDate = DateTime.Now, Customer = customer });
Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
if (customerBusiness.AddCustomer(customer))
{
this.RepeaterDataBind();
}
}
此时的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的情况
<!--一对多关系:一个客户可以有一个或者多个订单-->
<!--子实体负责维护关联关系-->
<set name="Orders" table="TB_Order" generic="true" inverse="true" 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
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'
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'
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
[NHibernate]一对多关系(级联删除,级联添加)的更多相关文章
- [NHibernate]一对多关系(关联查询)
目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关 ...
- [Fluent NHibernate]一对多关系处理
目录 写在前面 系列文章 一对多关系 总结 写在前面 上篇文章简单介绍了,Fluent Nhibernate使用代码的方式生成Nhibernate的配置文件,以及如何生成持久化类的映射文件.通过上篇的 ...
- 15.翻译系列:EF 6中的级联删除【EF 6 Code-First 系列】
原文链接:https://www.entityframeworktutorial.net/code-first/cascade-delete-in-code-first.aspx EF 6 Code- ...
- Dynamic CRM一对多关系的数据删除时设置自动删除关联的数据
在业务实体中主子表非常常见,然后子表可能有会有自己的子表或者多对多关系,在删除的业务场景下,删除主数据,剩余的子数据就成了脏数据, 之前的做法是,监听主表的删除事件,然后在插件中找到其下的子表数据然后 ...
- [NHibernate]多对多关系(关联查询)
目录 写在前面 文档与系列文章 多对多关系关联查询 总结 写在前面 上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题.这篇文章对多对多关系的查询处理 ...
- 五、hibernate表与表之间的关系(一对多关系)
数据库表与表之间的关系 一对多:一个学校可以有多个学生,一个学生只能有一个学校 多对多:一个学生可以有多个老师,一个老师可以教多个学生 一对一:一个人只能有一个身份证号,一个身份证号只能找到一个人 一 ...
- EF里一对一、一对多、多对多关系的配置和级联删除
本章节开始了解EF的各种关系.如果你对EF里实体间的各种关系还不是很熟悉,可以看看我的思路,能帮你更快的理解. I.实体间一对一的关系 添加一个PersonPhoto类,表示用户照片类 /// < ...
- EF——一对一、一对多、多对多关系的配置和级联删除 04(转)
EF里一对一.一对多.多对多关系的配置和级联删除 本章节开始了解EF的各种关系.如果你对EF里实体间的各种关系还不是很熟悉,可以看看我的思路,能帮你更快的理解. I.实体间一对一的关系 添加一个P ...
- 【JAVAEE学习笔记】hibernate03:多表操作详解、级联、关系维护和练习:添加联系人
一.一对多|多对一 1.关系表达 表中的表达 实体中的表达 orm元数据中表达 一对多 <!-- 集合,一对多关系,在配置文件中配置 --> <!-- name属性:集合属性名 co ...
随机推荐
- Linux第一天 ssh登录和软件安装详解
Linux学习第一天 操作环境: Ubuntu 16.04 Win10系统,使用putty_V0.63 本身学习Linux就是想在服务器上使用的.实际情况,可能我很难直接到坐在服务器前,使用界面操作系 ...
- java中执行系统命令
java程序中执行系统命令猛击下面的链接看看你就知道怎么用了 http://blog.csdn.net/a19881029/article/details/8063758 http://wuhongy ...
- linux下的守护进程daemon
什么是守护进程?其实感觉守护进程并没有什么明确的定义,只是守护进程有一些特征,这是它需要遵循的. 守护进程的第一个特征是长时间在后台运行的程序,并且主要是为了提供某种服务,而为了能够让服务尽可能随时都 ...
- spring的注入
1 可能遇到的问题: 异常信息 NoSuchBeanDefinitionException: No matching bean of type [...]或是NoSuchBeanDefinitionE ...
- String,StringBuffer,StringBuilder的区别
public static void main(String[] args) { String str = new String("hello...."); StringBuffe ...
- Oracle 增删改查
Oracle入门案例: 1.创建实体类Student 并重写ToString方法 package cn.happy.entity; public class Student { public Inte ...
- jquery easyui 1.4.1 API( CHM版)
ChangeLog Bug The combogrid has different height than other combo components. fixed. datagrid: The r ...
- 【转载】WEB前端开发规范文档
本文转载自谈笑涧<WEB前端开发规范文档> 为 新项目写的一份规范文档, 分享给大家. 我想前端开发过程中, 无论是团队开发, 还是单兵做站, 有一份开发文档做规范, 对开发工作都是很有益 ...
- 深入理解Java:类加载机制及反射
说明:本文乃学习整理参考而来. 一.Java类加载机制 1.概述 Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构 ...
- 用于制作app store的截图的工具:Brief Wrapper —— 最便捷的应用商店屏幕快照
https://itunes.apple.com/cn/app/brief-wrapper-zui-bian-jie/id991730319?l=en&mt=8 可以快捷的做出类似于下面的这种 ...