NHibernate学习手记(6) - 实现one2many/many2one的映射
2006-03-20 16:43
519 查看
一对多(one2many)是最常见的对象关系之一,本文将通过示例说明如何使用NH来实现one2many关系的映射,以及如何实现Parent/Child对象之间的级连操作。
根据约定,本文将通过Category和Item对象来描述one2many的关系,即一个Category对象对应多个Item对象。
主要内容:
1、编写POCO类
2、准备数据库
3、编写配置文件
4、级连(cascading)操作示例
一、编写POCO类
从手记(6)起我打算由编写POCO类开始描述,因为NHibernate已经让我们以对象的方式去思考数据操作,数据表该怎么设计已经不是思维的起点,更不是重点。
1、Category的POCO类:
1 using System;
2 using System.Collections;
3
4 namespace TestOne2Many
5 {
6 /// <summary>
7 /// Category 的摘要说明
8 /// </summary>
9 /// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
/// 版 本:
public class Category
{
private Guid _categoryId;
private string _name = string.Empty;
private IList _items;
public Guid CategoryID
{
get { return this._categoryId; }
set { this._categoryId = value; }
}
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public IList Items
{
get { return this._items; }
set { this._items = value; }
}
#region 构造函数
/// <summary>
/// 默认无参构造函数
/// </summary>
/// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
public Category()
{
this._items = new ArrayList();
}
#endregion
}
}
一对多的关系在.net代码里是以集合的形式去表示的,按照NH的online document的说法,NH支持三种类型的集合:System.Collections.IList、System.Collection.IDictionary、Iesi.Collections.ISet,在进行O/R mapping时,NH将自动把上述集合转化为NHibernate.Collection中对应的集合类型。
下面的测试代码无法通过,因为NH已经把Category.Items转化为NHibernate.Collection.Bag
1 [Test]
2 public void TestCollectionType()
3 {
4 using (ISession session = TestCategory.Factory.OpenSession())
5 {
6 // We assume that there are only one category object in the repository,
7 // see initialization in TestCategory.TestInitialize().
8 // note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
9 Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
// that works?
Assert.AreEqual(expectedCategory.Items.GetType(), typeof(System.Collections.ArrayList));
}
}
2、Item的POCO类:
1 using System;
2
3 namespace TestOne2Many
4 {
5 /// <summary>
6 /// Item 的摘要说明
7 /// </summary>
8 /// 创 建 人: Aero
9 /// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
/// 版 本:
public class Item
{
private Guid _itemId;
private string _name = string.Empty;
private Category _category;
public Guid ItemID
{
get { return this._itemId; }
set { this._itemId = value; }
}
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public Category Category
{
get { return this._category; }
set { this._category = value; }
}
#region 构造函数
/// <summary>
/// 默认无参构造函数
/// </summary>
/// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
public Item()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
#endregion
}
}
二、准备数据库
新建数据库nh_categories和nh_items,数据库设计如下:
或直接执行以下sql语句(本文示例代码所使用的数据库名称为NHTrial)
use NHTrial
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_categories_nh_items_FK1]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE [dbo].[nh_items] DROP CONSTRAINT nh_categories_nh_items_FK1
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_categories]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[nh_categories]
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_items]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[nh_items]
GO
CREATE TABLE [dbo].[nh_categories] (
[CategoryID] [uniqueidentifier] NOT NULL ,
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[nh_items] (
[ItemID] [uniqueidentifier] NOT NULL ,
[CategoryID] [uniqueidentifier] NOT NULL ,
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_categories] WITH NOCHECK ADD
CONSTRAINT [nh_categories_PK] PRIMARY KEY CLUSTERED
(
[CategoryID]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_items] WITH NOCHECK ADD
CONSTRAINT [nh_items_PK] PRIMARY KEY CLUSTERED
(
[ItemID]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_items] ADD
CONSTRAINT [nh_categories_nh_items_FK1] FOREIGN KEY
(
[CategoryID]
) REFERENCES [dbo].[nh_categories] (
[CategoryID]
)
GO
三、编写配置文件
1、新建hibernate.cfg.xml文件,设置hibernate的运行配置信息。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0" >
<session-factory name="TestOne2Many">
<!-- properties -->
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=localhost;database=NHTrial;User Id=sa;Password=sa</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="use_outer_join">true</property>
</session-factory>
</hibernate-configuration>
2、新建文件objects.hbm.xml,配置Category和Item类的o/r mapping信息,并且把objectes.hbm.xml的属性设置为“嵌入资源”。
子非鱼在NHibernate学习里说实体xxx的mapping信息要写在xxx.hbm.xml文件里面,其实nhibernate没有这个限制,完全可以把n个实体的配置信息写在一个hbm.xml文件中。
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
3 <class name="TestOne2Many.Category, TestOne2Many" table="nh_categories">
4 <id name="CategoryID" column="CategoryID" type="Guid"
5 unsaved-value="00000000-0000-0000-0000-000000000000">
6 <generator class="guid" />
7 </id>
8
9 <property name="Name" type="string" length="50" />
<bag name="Items" lazy="true" inverse="true" cascade= "all-delete-orphan">
<key column="CategoryID" />
<one-to-many class="TestOne2Many.Item, TestOne2Many" />
</bag>
</class>
<class name="TestOne2Many.Item, TestOne2Many" table="nh_items">
<id name="ItemID" column="ItemID" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid" />
</id>
<property name="Name" type="string" length="50" />
<many-to-one name="Category" class="TestOne2Many.Category, TestOne2Many"
column="CategoryID" />
</class>
</hibernate-mapping>
部分配置节点的含义和用法在NHibernate学习手记(5) - 简单的对象映射里已经说过了,这里只看看bag、one-to-many和many-to-one。
1)bag节点:用于定义System.Collection.IList的类型的集合元素。
2)级连操作选项说明:
当进行save-update级连操作时,NH将根据子对象主键的unsave-value来判断该执行save还是update操作。
3)key节点:用于指定nh_items表中用作外键(和nh_categories)的数据列名称
4)one-to-many节点:用于指定子对象的类型(全限定名称)
5)many-to-one节点:用于指定父对象属性,如Item.Category
四、示例one2many的级连操作。
看过子非鱼兄的NHibernate学习后,发现用单元测试来进行代码示例的确是一种非常有效的方式,大家只需要的是再增加一个NUnit.framework的引用:)。
1、级连添加:在保存新增的Category对象时,级连保存与之关联的Item对象
1 /// <summary>
2 /// demonstrate how to execute a the cascading save
3 /// </summary>
4 [Test]
5 public void TestCascadingSave()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // prepare test objects
Category expectedCategory = new Category();
expectedCategory.Name = "category" + System.Environment.TickCount.ToString();
for (int i = 0; i < 10; i++)
{
Item item = new Item();
item.Name = "item" + System.Environment.TickCount.ToString();
item.Category = expectedCategory;
expectedCategory.Items.Add(item);
}
// save objects in a all-cascading way
// note: cascading option should at least set as "all" in objects.hbm.xml
ITransaction trans = session.BeginTransaction();
try
{
session.SaveOrUpdate(expectedCategory);
trans.Commit();
}
catch
{
trans.Rollback();
throw;
}
// that works?
Category actualCategory =
session.Get(typeof(Category), expectedCategory.CategoryID) as Category;
Assert.IsNotNull(actualCategory);
Assert.AreEqual(expectedCategory.Items.Count, actualCategory.Items.Count);
}
}
2、级连更新(update):移除Category对象的部分Item子对象,保存更改后,被移除的Item子对象从数据库中删除。
1 /// <summary>
2 /// demonstrate how to remove sub-items and execute a cascading update
3 /// </summary>
4 [Test]
5 public void TestCascadingUpdate()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // We assume that there are only one category object in the repository,
// see initialization in TestCategory.TestInitialize().
// note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
int expectedItemCount = expectedCategory.Items.Count;
// execute a cascading update
ITransaction trans = session.BeginTransaction();
try
{
// remove an item from item-collection from the repository
expectedCategory.Items.RemoveAt(0);
session.Update(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(1, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(expectedItemCount - 1, session.CreateCriteria(typeof(Item)).List().Count);
}
}
3、级连删除:当父Category对象被删除,与之关联的Item对象也被删除。
1 /// <summary>
2 /// demonstrate how to execute a cascading deletion
3 /// </summary>
4 [Test]
5 public void TestCascadingDelete()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // We assume that there are only one category object in the repository,
// see initialization in TestCategory.TestInitialize().
// note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
// remove category from the repository
ITransaction trans = session.BeginTransaction();
try
{
session.Delete(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(0, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(0, session.CreateCriteria(typeof(Item)).List().Count);
}
}
要特殊指出的是,NH不支持通过Category.Item=null这种方式来删除与Category对象关联的Item对象。
1 [Test, ExpectedException(typeof(NHibernate.HibernateException))]
2 public void TestCascadingUpdateFail()
3 {
4 using (ISession session = TestCategory.Factory.OpenSession())
5 {
6 // We assume that there are only one category object in the repository,
7 // see initialization in TestCategory.TestInitialize().
8 // note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
9 Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
int expectedItemCount = expectedCategory.Items.Count;
// execute a cascading update
ITransaction trans = session.BeginTransaction();
try
{
// we can't remove all items by dereference Category.Items as null,
// this will cause a NHiberate.HibernateException
expectedCategory.Items = null;
// still we can't remove items in the following way,
// this will cause a NullReference Exception from NHibernate
//expectedCategory.Items[0] = null;
session.Update(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(1, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(expectedItemCount, session.CreateCriteria(typeof(Item)).List().Count);
}
}
完整示例代码可从ObjectMappings.rar下载,其中的TestOne2Many即本文所讨论的工程。
根据约定,本文将通过Category和Item对象来描述one2many的关系,即一个Category对象对应多个Item对象。
主要内容:
1、编写POCO类
2、准备数据库
3、编写配置文件
4、级连(cascading)操作示例
一、编写POCO类
从手记(6)起我打算由编写POCO类开始描述,因为NHibernate已经让我们以对象的方式去思考数据操作,数据表该怎么设计已经不是思维的起点,更不是重点。
1、Category的POCO类:
1 using System;
2 using System.Collections;
3
4 namespace TestOne2Many
5 {
6 /// <summary>
7 /// Category 的摘要说明
8 /// </summary>
9 /// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
/// 版 本:
public class Category
{
private Guid _categoryId;
private string _name = string.Empty;
private IList _items;
public Guid CategoryID
{
get { return this._categoryId; }
set { this._categoryId = value; }
}
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public IList Items
{
get { return this._items; }
set { this._items = value; }
}
#region 构造函数
/// <summary>
/// 默认无参构造函数
/// </summary>
/// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
public Category()
{
this._items = new ArrayList();
}
#endregion
}
}
一对多的关系在.net代码里是以集合的形式去表示的,按照NH的online document的说法,NH支持三种类型的集合:System.Collections.IList、System.Collection.IDictionary、Iesi.Collections.ISet,在进行O/R mapping时,NH将自动把上述集合转化为NHibernate.Collection中对应的集合类型。
下面的测试代码无法通过,因为NH已经把Category.Items转化为NHibernate.Collection.Bag
1 [Test]
2 public void TestCollectionType()
3 {
4 using (ISession session = TestCategory.Factory.OpenSession())
5 {
6 // We assume that there are only one category object in the repository,
7 // see initialization in TestCategory.TestInitialize().
8 // note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
9 Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
// that works?
Assert.AreEqual(expectedCategory.Items.GetType(), typeof(System.Collections.ArrayList));
}
}
2、Item的POCO类:
1 using System;
2
3 namespace TestOne2Many
4 {
5 /// <summary>
6 /// Item 的摘要说明
7 /// </summary>
8 /// 创 建 人: Aero
9 /// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
/// 版 本:
public class Item
{
private Guid _itemId;
private string _name = string.Empty;
private Category _category;
public Guid ItemID
{
get { return this._itemId; }
set { this._itemId = value; }
}
public string Name
{
get { return this._name; }
set { this._name = value; }
}
public Category Category
{
get { return this._category; }
set { this._category = value; }
}
#region 构造函数
/// <summary>
/// 默认无参构造函数
/// </summary>
/// 创 建 人: Aero
/// 创建日期: 2006-3-17
/// 修 改 人:
/// 修改日期:
/// 修改内容:
public Item()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
#endregion
}
}
二、准备数据库
新建数据库nh_categories和nh_items,数据库设计如下:
或直接执行以下sql语句(本文示例代码所使用的数据库名称为NHTrial)
use NHTrial
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_categories_nh_items_FK1]') and OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE [dbo].[nh_items] DROP CONSTRAINT nh_categories_nh_items_FK1
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_categories]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[nh_categories]
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[nh_items]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[nh_items]
GO
CREATE TABLE [dbo].[nh_categories] (
[CategoryID] [uniqueidentifier] NOT NULL ,
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[nh_items] (
[ItemID] [uniqueidentifier] NOT NULL ,
[CategoryID] [uniqueidentifier] NOT NULL ,
[Name] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_categories] WITH NOCHECK ADD
CONSTRAINT [nh_categories_PK] PRIMARY KEY CLUSTERED
(
[CategoryID]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_items] WITH NOCHECK ADD
CONSTRAINT [nh_items_PK] PRIMARY KEY CLUSTERED
(
[ItemID]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[nh_items] ADD
CONSTRAINT [nh_categories_nh_items_FK1] FOREIGN KEY
(
[CategoryID]
) REFERENCES [dbo].[nh_categories] (
[CategoryID]
)
GO
三、编写配置文件
1、新建hibernate.cfg.xml文件,设置hibernate的运行配置信息。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.0" >
<session-factory name="TestOne2Many">
<!-- properties -->
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Server=localhost;database=NHTrial;User Id=sa;Password=sa</property>
<property name="show_sql">false</property>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="use_outer_join">true</property>
</session-factory>
</hibernate-configuration>
2、新建文件objects.hbm.xml,配置Category和Item类的o/r mapping信息,并且把objectes.hbm.xml的属性设置为“嵌入资源”。
子非鱼在NHibernate学习里说实体xxx的mapping信息要写在xxx.hbm.xml文件里面,其实nhibernate没有这个限制,完全可以把n个实体的配置信息写在一个hbm.xml文件中。
1 <?xml version="1.0" encoding="utf-8" ?>
2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
3 <class name="TestOne2Many.Category, TestOne2Many" table="nh_categories">
4 <id name="CategoryID" column="CategoryID" type="Guid"
5 unsaved-value="00000000-0000-0000-0000-000000000000">
6 <generator class="guid" />
7 </id>
8
9 <property name="Name" type="string" length="50" />
<bag name="Items" lazy="true" inverse="true" cascade= "all-delete-orphan">
<key column="CategoryID" />
<one-to-many class="TestOne2Many.Item, TestOne2Many" />
</bag>
</class>
<class name="TestOne2Many.Item, TestOne2Many" table="nh_items">
<id name="ItemID" column="ItemID" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid" />
</id>
<property name="Name" type="string" length="50" />
<many-to-one name="Category" class="TestOne2Many.Category, TestOne2Many"
column="CategoryID" />
</class>
</hibernate-mapping>
部分配置节点的含义和用法在NHibernate学习手记(5) - 简单的对象映射里已经说过了,这里只看看bag、one-to-many和many-to-one。
1)bag节点:用于定义System.Collection.IList的类型的集合元素。
Attributes | Usage | Example |
name | 指示映射的属性名称。Required | Items (指Category.Items) |
lazy | 指示是否使用延迟加载。Optional | true | false |
cascade | 指示级连操作类型。Optional | all |
value | usage |
none | 默认值,不进行级连操作。 |
save-update | 进行级连save和update操作 |
delete | 进行级连删除操作 |
delete-orphan | 删除无相关的父对象的子对象 |
all | 进行级连save/update/delete操作 |
all-delete-orphan | all + delete-orphan |
当进行save-update级连操作时,NH将根据子对象主键的unsave-value来判断该执行save还是update操作。
3)key节点:用于指定nh_items表中用作外键(和nh_categories)的数据列名称
4)one-to-many节点:用于指定子对象的类型(全限定名称)
5)many-to-one节点:用于指定父对象属性,如Item.Category
Attributes | Usage | Example |
name | 指示映射的属性名称。Required | Category (指Item.Category) |
class | 指示指示父对象的全限定名称。Required | TestOne2Many.Category, TestOne2Many |
column | 指示子表的外键列名称。Required | CategoryID |
看过子非鱼兄的NHibernate学习后,发现用单元测试来进行代码示例的确是一种非常有效的方式,大家只需要的是再增加一个NUnit.framework的引用:)。
1、级连添加:在保存新增的Category对象时,级连保存与之关联的Item对象
1 /// <summary>
2 /// demonstrate how to execute a the cascading save
3 /// </summary>
4 [Test]
5 public void TestCascadingSave()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // prepare test objects
Category expectedCategory = new Category();
expectedCategory.Name = "category" + System.Environment.TickCount.ToString();
for (int i = 0; i < 10; i++)
{
Item item = new Item();
item.Name = "item" + System.Environment.TickCount.ToString();
item.Category = expectedCategory;
expectedCategory.Items.Add(item);
}
// save objects in a all-cascading way
// note: cascading option should at least set as "all" in objects.hbm.xml
ITransaction trans = session.BeginTransaction();
try
{
session.SaveOrUpdate(expectedCategory);
trans.Commit();
}
catch
{
trans.Rollback();
throw;
}
// that works?
Category actualCategory =
session.Get(typeof(Category), expectedCategory.CategoryID) as Category;
Assert.IsNotNull(actualCategory);
Assert.AreEqual(expectedCategory.Items.Count, actualCategory.Items.Count);
}
}
2、级连更新(update):移除Category对象的部分Item子对象,保存更改后,被移除的Item子对象从数据库中删除。
1 /// <summary>
2 /// demonstrate how to remove sub-items and execute a cascading update
3 /// </summary>
4 [Test]
5 public void TestCascadingUpdate()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // We assume that there are only one category object in the repository,
// see initialization in TestCategory.TestInitialize().
// note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
int expectedItemCount = expectedCategory.Items.Count;
// execute a cascading update
ITransaction trans = session.BeginTransaction();
try
{
// remove an item from item-collection from the repository
expectedCategory.Items.RemoveAt(0);
session.Update(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(1, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(expectedItemCount - 1, session.CreateCriteria(typeof(Item)).List().Count);
}
}
3、级连删除:当父Category对象被删除,与之关联的Item对象也被删除。
1 /// <summary>
2 /// demonstrate how to execute a cascading deletion
3 /// </summary>
4 [Test]
5 public void TestCascadingDelete()
6 {
7 using (ISession session = TestCategory.Factory.OpenSession())
8 {
9 // We assume that there are only one category object in the repository,
// see initialization in TestCategory.TestInitialize().
// note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
// remove category from the repository
ITransaction trans = session.BeginTransaction();
try
{
session.Delete(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(0, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(0, session.CreateCriteria(typeof(Item)).List().Count);
}
}
要特殊指出的是,NH不支持通过Category.Item=null这种方式来删除与Category对象关联的Item对象。
1 [Test, ExpectedException(typeof(NHibernate.HibernateException))]
2 public void TestCascadingUpdateFail()
3 {
4 using (ISession session = TestCategory.Factory.OpenSession())
5 {
6 // We assume that there are only one category object in the repository,
7 // see initialization in TestCategory.TestInitialize().
8 // note: cascading option should set as "all-delete-orphan" in objects.hbm.xml
9 Category expectedCategory =
session.CreateCriteria(typeof(Category)).List()[0] as Category;
int expectedItemCount = expectedCategory.Items.Count;
// execute a cascading update
ITransaction trans = session.BeginTransaction();
try
{
// we can't remove all items by dereference Category.Items as null,
// this will cause a NHiberate.HibernateException
expectedCategory.Items = null;
// still we can't remove items in the following way,
// this will cause a NullReference Exception from NHibernate
//expectedCategory.Items[0] = null;
session.Update(expectedCategory);
trans.Commit();
}
catch (System.Exception e)
{
trans.Rollback();
throw;
}
// that works?
Assert.AreEqual(1, session.CreateCriteria(typeof(Category)).List().Count);
Assert.AreEqual(expectedItemCount, session.CreateCriteria(typeof(Item)).List().Count);
}
}
完整示例代码可从ObjectMappings.rar下载,其中的TestOne2Many即本文所讨论的工程。
相关文章推荐
- NHibernate学习手记(6) - 实现one2many/many2one的映射
- NHibernate学习手记(6) - 实现one2many/many2one的映射
- NHibernate学习手记(6) - 实现one2many/many2one的映射
- NHibernate学习手记(6) - 实现one2many/many2one的映射
- NHibernate学习手记(5) - 简单的对象映射
- NHibernate学习手记(5) - 简单的对象映射
- NHibernate学习手记(5) - 简单的对象映射
- NHibernate学习手记(5) - 简单的对象映射
- 从零开始MDT2010学习手记(九) 结合组策略实现Windows XP升级到Windows 7
- NHibernate学习手记(4) - 持久化类(Persistent class)的设计
- Atlas学习手记(4):使用AutoComplete Extender实现自动完成功能
- Atlas学习手记(18):使用DragPanel实现拖放面板
- [原创]java WEB学习笔记84:Hibernate学习之路-- -映射 一对一关系 ,基外键的方式实现
- Atlas学习手记(7):使用DragOverlayExtender实现拖放功能
- Castle ActiveRecord学习实践(4):实现One-Many关系的映射
- NHibernate学习手记(1) - 对象的简单CRUD操作
- Atlas学习手记(7):使用DragOverlayExtender实现拖放功能
- NHibernate学习(7)—对于实现机理的猜测
- UNIX环境高级编程学习之第十五章进程间通信 - 两个进程通过映射普通文件实现共享内存通信
- 学习使用NHibernate2.1.0Beta1(三)— 创建映射类库