本文约定:
1. Nhibernate 简写为 NHB;
2. 本文例子的开发平台为 win2000xp+sp2, sql server2000, Nhibernate0.9.1.0;
3. 使用 SQL Server 自带的罗斯文商贸数据库 (Northwind), 中文版;
4. 本文例子是基于测试驱动开发(TDD)的,因此建议使用 NUnit2.2 和 Log4Net (如果你不熟悉 NUnit,不要紧啊,趁此机会学习点简单的应用);
一 NHB 简介
NHB 是基于 ms.net 的 O/R Mapping 持久框架,它从基于 Java 的 Hibernate 项目移植而来.O/R Mapping 就是把对象到映射关系数据库的记录,简单的说就是能实现把一个对象存储为数据表中的一条记录和由一条记录创建一个相应的对象,数据表中的数据就是对象的属性.
那么为什么要使用 O/R Mapping?它与传统的 DataSet/DataTable 又有什么不同了?
首先是设计上的不同,当使用 O/R Mapping 时,更多的是从对象的角度来设计程序,而把数据 (对象的属性) 存储的细节放在后面, 可以完全采用面向对象 (OO) 的方式来设计,而在使用 DataSet/DataTable 时,它只是存放数据的对象,看起来更像一个数据表,不能直观的表达业务概念.
二 NHB 中主要接口的介绍
ISession
ISession 是面向用户的主要接口,主要用于对象持久化,数据加载等操作,支持数据库事务,它隐藏了 NHB 内部复杂的实现细节,ISession 由 ISessionFactory 创建.
ISessionFactory
ISessionFactory 是 NHB 内部的核心类,它维护到持久机制 (数据库) 的连接并对它们进行管理,同时还会保存所有持久对象的映射信息.
ISessionFactory 由 Configuration 创建,因为创建 ISessionFactory 的开销非常大(需要加载映射信息),所以这个对象一般使用 Singleton(单例)模式.
ITransaction
ITransaction 是 NHB 的事务处理接口,它只是简单的封装了底层的数据库事务.
事务必须由 ISession 来启动.
ICriteria
ICriteria 是 Expression(表达式) 数据加载接口,Expression 是一个关系表达式组合,通过它能产生 SQL 语句的 Where 部分, 用户需要通过 ISession 来间接调用它.
IQuery
IQuery 是 HQL 数据加载接口,HQL(Hibernate Query Language) 是 NHB 专用的面向对象的数据查询语言,它与数据库的 SQL 有些类似,但功能更强大!同 ICriteria 一样,也需要通过 ISession 来间接调用它.
三 持久化操作
1. 会话和会话工厂
要进行持久化操作,必须先取得 ISession 和 ISessionFactory,我们用一个 Sessions 类来封装它们, Sessions 类的属性和方法都是静态的,它有一个 Factory 属性, 用于返回 ISessionFactory, 有一个 GetSession 方法,用于取得一个新的 ISession.
测试类代码如下:
using System;
using NUnit.Framework;
using NHibernate;
namespace NHibernateTest {
////// SessionsFixture 的摘要说明.
////// [TestFixture]
public class SessionsFixture {
public SessionsFixture() {
//// TODO: 在此处添加构造函数逻辑
//
} [Test] // 测试能否取得NHB会话工厂. public void FactoryTest()
{
ISessionFactory sf = Sessions.Factory;
Assert.IsNotNull(sf, "get sessionfactory fail!");
} [Test] // 测试能否取得NHB会话. public void GetSessionTest()
{
ISession s = Sessions.GetSession();
Assert.IsNotNull(s, "get session fail!");
}
}
}
现在还没写 Sessions 类,将不能通过编译! 下面我们来实现 Sessions 类.
using System;
using NHibernate;
using System.Reflection;
namespace NHibernateTest {
////// Sessions 的摘要说明.
/// public class Sessions
{
private static readonly object lockObj = new object();
private static ISessionFactory _factory;
public Sessions() {
//// TODO: 在此处添加构造函数逻辑
//
}
public static ISessionFactory Factory {
get {
if (_factory == null) {
lock(lockObj) {
if (_factory == null) {
NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
_factory = cfg.BuildSessionFactory();
}
}
}
return _factory;
}
}
public static ISession GetSession() {
return Factory.OpenSession();
}
}
}
OK,现在编译可以通过了,启动 NUnit 并选择生成的文件 NHibernateTest.exe, 运行测试.我们得到了红色的条,出错了!原来还没有加入 NHibernate 的配置信息(当使用 NHibernate 时,需要在项目的配置文件中加入 NHibernate 的配置信息.关于配置信息,在下面有说明).在项目的配置文件 App.Config(如没有请自行创建一个) 中加入以下内容.
稍做解释,下面的配置文件主要是配置了 NHibernate 和 log4net,例如配置了 NHibernate 连接数据库的连接字符串;log4net 把日志记到哪个文件中,本例就是 "log.txt".每次写日志向文件中追加,而不是重写
xml version = "1.0"encoding = "utf-8" ? ><configuration > <configSections > <section name = "nhibernate"type = "System.Configuration.
NameValueSectionHandler, System, Version=1.0.5000.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089" / ><section name = "log4net"type = "log4net.Config.
Log4NetConfigurationSectionHandler,log4net" / >configSections > <nhibernate > <add key = "hibernate.show_sql"value = "true" / ><add key = "hibernate.connection.provider"value = "NHibernate.Connection.DriverConnectionProvider" / ><add key = "hibernate.dialect"value = "NHibernate.Dialect.MsSql2000Dialect" / ><add key = "hibernate.connection.driver_class"value = "NHibernate.Driver.SqlClientDriver" / ><add key = "hibernate.connection.connection_string"value = "Server=127.0.0.1;initial catalog=Northwind;User id =golinjoe;
Password=2525775" / >nhibernate > <log4net > <appender name = "rollingFile"type = "log4net.Appender.RollingFileAppender,
log4net" > <param name = "File"value = "log.txt" / ><param name = "AppendToFile"value = "true" / ><param name = "RollingStyle"value = "Date" / ><param name = "DatePattern"value = "yyyy.MM.dd" / ><param name = "StaticLogFileName"value = "true" / ><layout type = "log4net.Layout.PatternLayout,log4net" > <param name = "ConversionPattern"value = "%d [%t] %-5p %c [%x] <%X{auth}> - %m%n" / >layout > appender > <root > <priority value = "ALL" / ><appender - ref ref = "rollingFile" / >root > log4net > configuration >
再次运行测试,就可以看见绿色的条了.
在取得会话工厂的代码中,我使用了如下代码:
if (_factory == null) {
lock (lockObj) {
if (_factory == null) {
// build sessionfactory code;
}
}
}
这是一个典型的 double lock 方式,用来产生线程安全的 Singletion(单例)对象.
2. 基本 CRUD 操作
在很多介绍 NHB 的文章,包括 NHB 带的测试用例中,业务对象只是做为一个数据实体存在的,它没有任何操作!这在 java 中是比较典型的作法.
而我希望我们的业务对象自身就能完成基本的 Create/Retrieve/Update/Delete,即 CRUD 操作,
在罗斯文商贸应用中,存在客户 (customer) 业务对象,先来为它建立一个测试用例,
using System;
using NHibernate;
using NUnit.Framework;
namespace NHibernateTest {
////// CustomerFixture 的摘要说明.
/// [TestFixture]
public class CustomerFixture {
public CustomerFixture() {} [Test] // 测试Customer对象的CRUD操作. public void TestCRUD()
{
Customer c = new Customer();
c.CustomerId = "test";
c.CompanyName = "company name";
c.ContactName = "contact name";
c.Address = "address";
c.Create(); // 测试 insert操作,
Customer c2 = new Customer(c.CustomerId); // 测试 retrieve 操作.
Assert.AreEqual(c2.CompanyName, "company name", "save companyname fail! ");
c2.CompanyName = "update name";
c2.Update(); // 测试 update 操作.
Customer c3 = new Customer(c.CustomerId);
Assert.AreEqual(c3.CompanyName, "update name", "update companyname fail! ");
c3.Delete(); // 测试 delete 操作. }
}
}
接下来创建 Customer 业务类:
using System;
namespace NHibernateTest {
////// Customer 的摘要说明.
/// public class Customer : BizObject
{
public Customer() {}
public Customer(string existingId) : base(existingId) {}#region persistent properties.private string _customerId = string.Empty;
private string _companyName = string.Empty;
private string _contactName = string.Empty;
private string _contactTitle = string.Empty;
private string _address = string.Empty;
private string _city = string.Empty;
private string _region = string.Empty;
private string _postalCode = string.Empty;
private string _country = string.Empty;
private string _phone = string.Empty;
private string _fax = string.Empty;
public string CustomerId {
get {
return _customerId;
}
set {
_customerId = value;
}
}
public string CompanyName {
get {
return _companyName;
}
set {
_companyName = value;
}
}
public string ContactName {
get {
return _contactName;
}
set {
_contactName = value;
}
}
public string ContactTitle {
get {
return _contactTitle;
}
set {
_contactTitle = value;
}
}
public string Address {
get {
return _address;
}
set {
_address = value;
}
}
public string City {
get {
return _city;
}
set {
_city = value;
}
}
public string Region {
get {
return _region;
}
set {
_region = value;
}
}
public string PostalCode {
get {
return _postalCode;
}
set {
_postalCode = value;
}
}
public string Country {
get {
return _country;
}
set {
_country = value;
}
}
public string Phone {
get {
return _phone;
}
set {
_phone = value;
}
}
public string Fax {
get {
return _fax;
}
set {
_fax = value;
}
}#endregion
}
}
在 Customer 类中,没有实现 CRUD 操作,这些操作在业务对象基类 BizObject 中实现,代码如下:
using System;
namespace NHibernateTest {
////// BizObject 的摘要说明.
/// public class BizObject
{
public BizObject() {}
public BizObject(object existingId) {
ObjectBroker.Load(this, existingId);
}
public virtual void Create() {
ObjectBroker.Create(this);
}
public virtual void Update() {
ObjectBroker.Update(this);
}
public virtual void Delete() {
ObjectBroker.Delete(this);
}
}
}
BizObject 简单的将数据操作转发至 ObjectBroker 类, 目的是为了降低业务层和 NHB 之间的耦合, 以利于持久层间的移植.
using System;
using NHibernate;
using NHibernateTest;
namespace NHibernateTest {
////// ObjectBroker 的摘要说明.
/// public class ObjectBroker
{
private ObjectBroker() {}
public static void Load(object obj, object id) {
ISession s = Sessions.GetSession();
try {
s.Load(obj, id);
} finally {
s.Close();
}
}
public static void Create(object obj) {
ISession s = Sessions.GetSession();
ITransaction trans = null;
try {
trans = s.BeginTransaction();
s.Save(obj);
trans.Commit();
} finally {
s.Close();
}
}
public static void Update(object obj) {
ISession s = Sessions.GetSession();
ITransaction trans = null;
try {
trans = s.BeginTransaction();
s.Update(obj);
trans.Commit();
} finally {
s.Close();
}
}
public static void Delete(object obj) {
ISession s = Sessions.GetSession();
ITransaction trans = null;
try {
trans = s.BeginTransaction();
s.Delete(obj);
trans.Commit();
} finally {
s.Close();
}
}
}
}
ObjectBroker 对 ISession 进行了必要的封装,通过 ISession, 就可以简单的完成对象的 CRUD 操作了.
编译并运行测试,CustomerFixture 的 TestCRUD 操作还是不能通过! 异常信息为:
NHibernateTest.CustomerFixture.TestCRUD : NHibernate.MappingException : Unknown entity class: NHibernateTest.Customer
显然,是因为我们还没有为 Customer 对象编写映射文件,而导致 NHB 不能对 Customer 对象进行持久化操作.
Customer 对象的映射文件 (Customer.hbm.xml) 内容如下:
xml version = "1.0"encoding = "utf-8" ? ><hibernate - mapping xmlns = "urn:nhibernate-mapping-2.0" > <class name = "NHibernateTest.Customer,NHibernateTest"table = "Customers" > <id name = "CustomerId"column = "customerId"type = "String"unsaved - value = "" > <generator class = "assigned" / >id > <property name = "CompanyName"column = "companyName"type = "String" / ><property name = "ContactName"column = "contactName"type = "String" / ><property name = "ContactTitle"column = "contactTitle"type = "String" / ><property name = "Address"column = "address"type = "String" / ><property name = "City"column = "city"type = "String" / ><property name = "Region"column = "region"type = "String" / ><property name = "PostalCode"column = "postalCode"type = "String" / ><property name = "Country"column = "country"type = "String" / ><property name = "Phone"column = "phone"type = "String" / ><property name = "Fax"column = "fax"type = "String" / >class > hibernate - mapping >
这个映射文件算是 NHB 中较为简单的了.
class 的 name 指定业务对象全名及其所在程序集,table 指定数据表的名称;
id 用于指定一个对象标识符(数据表中的主键)及其产生的方式, 常用的主健产生方式有自增型 (identity) 和赋值型(assigned),这里使用了 assigned,需要注意的是 unsaved-value 属性,它指定对象没有持久化时的 Id 值,主要用于 SaveOrUpdate 操作;
property 用于指定其它映射的数据列;
在 id 和 property 中,name 指定属性名称,column 指定数据列的名称,type 指定属性类型,注意这里的类型是 NHB 中的类型,而不是. NET 或数据库中的数据类型.
另外,对象映射文件名称请按 "对象名. hbm.xml" 的规范来命名, 最后在映射文件的属性中把操作改为 "嵌入的资源"(特别注意这点很重要,我就是没有注意到这.郁闷了很久啊!!!).
现在重新编译程序并运行测试,就能看到绿条了!
因为 Product 对象将在后面的案例中多次使用,在这里按与 Customer 相同的步骤创建它.
// Product 单元测试
using System;
using NUnit.Framework;
namespace NHibernateTest {
////// ProductFixture 的摘要说明.
/// [TestFixture]
public class ProductFixture {
public ProductFixture() {} [Test] // 测试Product对象的CRUD操作. public void TestCRUD()
{
Category c = null;
try {
c = new Category();
c.CategoryName = "test";
c.Create();
Product p = new Product();
p.ProductName = "test";
p.Category = c;
p.SupplierId = 3;
p.QuantityPerUnit = "1箱10只";
p.UnitPrice = 10.5M;
p.Create();
Product p2 = new Product(p.ProductId);
p2.UnitPrice = 15.8M;
p2.Update();
Product p3 = new Product(p.ProductId);
Assert.AreEqual(p3.UnitPrice, 15.8M, "update fail! ");
p3.Delete();
} finally {
if (c != null && c.CategoryId > 0) c.Delete();
}
}
}
}
using System;
namespace NHibernateTest {
////// Product 的摘要说明.
/// public class Product : BizObject
{
public Product() : base() {}
public Product(int existingId) : base(existingId) {}#region persistent properties private int _productId = 0;
private string _productName = string.Empty;
private int _supplierId = 0; // 应使用many-to-one, 需要重构.
// private int _categoryId = 0; // 应使用many-to-one, 需要重构.
private Category _category;
private string _quantityPerUnit = string.Empty;
private decimal _unitPrice = 0;
private int _unitsInStock = 0;
private int _unitsOnOrder = 0;
private int _reorderLevel = 0;
private bool _discontinued = false;
public int ProductId {
get {
return _productId;
}
set {
_productId = value;
}
}
public string ProductName {
get {
return _productName;
}
set {
_productName = value;
}
}
public int SupplierId {
get {
return _supplierId;
}
set {
_supplierId = value;
}
}
// public int CategoryId
// {
// get { return _categoryId; }
// set { _categoryId = value; }
// } public Category Category
{
get {
return _category;
}
set {
_category = value;
}
}
public string QuantityPerUnit {
get {
return _quantityPerUnit;
}
set {
_quantityPerUnit = value;
}
}
public decimal UnitPrice {
get {
return _unitPrice;
}
set {
_unitPrice = value;
}
}
public int UnitsInStock {
get {
return _unitsInStock;
}
set {
_unitsInStock = value;
}
}
public int UnitsOnOrder {
get {
return _unitsOnOrder;
}
set {
_unitsOnOrder = value;
}
}
public int ReorderLevel {
get {
return _reorderLevel;
}
set {
_reorderLevel = value;
}
}
public bool Discontinued {
get {
return _discontinued;
}
set {
_discontinued = value;
}
}#endregion
}
}
// Product 对象 (注意,注释部分并不是无意义的,是在不同的情况下做测试用的,本文中的注释都基本如此)
// 映射文件
xml version = "1.0"encoding = "utf-8" ? ><hibernate - mapping xmlns = "urn:nhibernate-mapping-2.0" > <class name = "NHibernateTest.Product, NHibernateTest"table = "Products" > <id name = "ProductId"column = "productId"type = "Int32"unsaved - value = "0" > <generator class = "identity" / >id > <property name = "ProductName"column = "ProductName"type = "String" / ><property name = "QuantityPerUnit"column = "QuantityPerUnit"type = "String" / ><property name = "UnitPrice"column = "unitPrice"type = "Decimal" / ><property name = "UnitsInStock"column = "unitsInStock"type = "Int32" / ><property name = "UnitsOnOrder"column = "unitsOnOrder"type = "Int32" / ><property name = "ReorderLevel"column = "reorderLevel"type = "Int32" / ><property name = "Discontinued"column = "discontinued"type = "Boolean" / ><property name = "SupplierId"column = "SupplierId"type = "Int32" / ><many - to - one name = "Category"column = "categoryId"unique = "true"class = "NHibernateTest.Category, NHibernateTest" / >class > hibernate - mapping >
编译并运行测试,检查错误直到单元测试通过.
注意:因为在数据库中,products 表与 categories 表,suppliers 表有外键约束,必须先删除这两个约束,product 测试用例才能通过,后面我们再加上这两个约束.
现在我们已经掌握了 NHB 的基本 CRUD 操作了,整个过程应该说是比较简单吧.呵呵,不再需要使用 Connection,DataAdapter,DataSet/DataReader 之类的对象了,下面继续学习 NHB 中更为复杂的映射关系.
3. one-to-one
一对一是一种常见的数据模型,它有两种情况:一种是主键 (PrimaryKey) 关联;另一种是外健 (ForeignKey) 关联,在使用外健的时候要保证其唯一性.
在主键关联的情况下, 必须有一个主键是根据别一个主键而来的.NHB 是通过一种特殊的方式来处理这种情况的, 要注意两个主健名称必须同名,而外健关联需要在 one-to-one 配置中定义一个 property-ref 属性, 这个属性在当前版本的 NHB(这是指的是 nhibernate 0.5.3, 当前的版本是什么情况我现在也没有时间了解)中还没有实现.
在罗斯文商贸应用,不需要使用 one-to-one 映射,这里先不对其进行讲解,如欲了解 one-to-one 方面的应用,请参考我网站上的文章.
4. many-to-one
many-to-one 是描述多对一的一种数据模型,它指定 many 一方是不能独立存在的,我个人认为 many-to-one 是 NHB 中保证数据有效性的最有用的一种映射,通过使用 many-to-one 能有效的防治孤儿记录被写入到数据表中.
在罗斯文商贸数据中,Product(产品) 与 Category(类别) 是多对一的关系.下面我们来处理这一映射关系,
首先要让 Category 能实现基本的 CRUD 操作,步骤同上, 这里只列出测试用例,类和映射文件请参照上面的方式创建.
[TestFixture] public class CategoryFixture {
public CategoryFixture() {} [Test] // 测试基本的CRUD操作. public void TestCRUD()
{
Category c = new Category();
c.CategoryName = "category1";
c.Description = "category1";
c.Create();
Category c2 = new Category(c.CategoryId);
c2.CategoryName = "testupdated";
c2.Update();
Category c3 = new Category(c.CategoryId);
Assert.AreEqual(c3.CategoryName, "testupdated", "update fail! ");
c3.Delete();
}
}
上面的测试用例通过后,接着修改 Product 的各部分.
Product 测试用例修改如下:
Product 类做如下修改:
1. 删除 categoryId 字段和 CategoryId 属性;
2. 加入以下代码:
public Category Category {
get {return _category;}
set {_category = value;}
}
private Category _category;
重新编译程序,运行测试用例, 看到绿条了吗?没有就根据异常去除错吧!我已经看到绿条了.:)
声明: 因为过多的 many-to-one 使后面的测试代码变得异常庞大(创建对象时要创建 one 一方的类),所以在后面的代码中,我假定 Product 对象是没有实现任何 many-to-one 映射的.如果不怕麻烦,请自行修改后面测试用例.
5. one-to-many
一对多也是一种常见的数据模型,在按范式设计的数据库中随处可见.在 NHB 中通过 one-to-many 可以非常方便的处理这种模型,同时 NHB 还提供了级联更新和删除的功能,以保证数据完整性.
在罗斯文商贸案例中,Customer 与 Order(订单),Order 和 OrderItem(订单项目) 就是一对多的关系,值得注意的是,并不是所有一对多关系都应该在 NHB 中实现,这取决于实际的需求情况,无谓的使用 one-to-many 映射只会降低 NHB 的使用性能.
下面先让 Order 和 OrderItem 对象能单独的完成 CRUD 操作,按上面处理 Customer 的方法来创建测试用例和类,
using System;
using NUnit.Framework;
using System.Collections;
namespace NHibernateTest {
////// OrderFixture 的摘要说明.
/// [TestFixture]
public class OrderFixture {
public OrderFixture() {} [Test] // 测试Order对象的CRUD操作. public void TestCRUD()
{
Order o = new Order();
o.CustomerId = "ALFKI";
o.EmployeeId = 1;
o.RequiredDate = new DateTime(2005, 9, 10);
o.ShippedDate = new DateTime(2005, 8, 28);
o.ShipVia = 1;
o.Freight = 20.5M;
o.ShipName = "test name";
o.ShipAddress = "test address";
o.Create();
Order o2 = new Order(o.OrderId);
o2.Freight = 21.5M;
o2.ShipAddress = "update address";
o2.Update();
Order o3 = new Order(o.OrderId);
Assert.AreEqual(o3.Freight, 21.5M, "update order fail! ");
Assert.AreEqual(o3.ShipAddress, "update address", "update order fail! ");
o3.Delete();
} [Test] // 测试OrderItem对象的CRUD操作.
public void TestItemCRUD() {
Order o = null;
Product p = null;
try {
o = new Order();
o.RequiredDate = new DateTime(2005, 9, 10);
o.ShippedDate = new DateTime(2005, 8, 28);
o.CustomerId = "ALFKI";
o.EmployeeId = 1;
o.ShipVia = 1;
o.Freight = 20.5M;
o.ShipName = "test name";
o.ShipAddress = "test address";
o.Create();
p = new Product();
p.ProductName = "test";
p.UnitPrice = 11.1M;
p.SupplierId = 3;
p.Create();
OrderItem item = new OrderItem();
// item.OrderId = 10248; item.Order = o;
// item.ProductId = 1; item.Product = p;
item.UnitPrice = 10.5M;
item.Quantity = 12;
item.Discount = 1;
item.Create();
OrderItem item2 = new OrderItem(item.ItemId);
item2.Quantity = 13;
item2.Update();
OrderItem item3 = new OrderItem(item.ItemId);
Assert.AreEqual(item3.Quantity, 13, "update orderitem fail! ");
item3.Delete();
} finally {
if (o != null && o.OrderId > 0) o.Delete();
if (p != null && p.ProductId > 0) p.Delete();
}
} [Test] public void TestOrderItem() {
Product p = null;
Product p2 = null;
try {
p = new Product();
p.ProductName = "test";
p.UnitPrice = 11.1M;
p.SupplierId = 3;
p.Create();
p2 = new Product();
p2.ProductName = "test2";
p2.UnitPrice = 18.1M;
p2.SupplierId = 3;
p2.Create();
Order o = new Order();
o.CustomerId = "ALFKI";
o.EmployeeId = 1;
o.RequiredDate = new DateTime(2005, 9, 10);
o.ShippedDate = new DateTime(2005, 8, 28);
o.ShipVia = 1;
o.Freight = 20.5M;
o.ShipName = "test name";
o.ShipAddress = "test address";
o.Create();
OrderItem item = new OrderItem();
item.Product = p;
item.UnitPrice = 10;
item.Quantity = 5 OrderItem item2 = new OrderItem();
item2.Product = p2;
item2.UnitPrice = 11;
item2.Quantity = 4;
o.AddItem(item);
o.AddItem(item2);
o.Create();
Order o2 = new Order(o.OrderId);
Assert.IsNotNull(o2.Items, "add item fail! ");
Assert.AreEqual(o2.Items.Count, 2, "add item fail! ");
IEnumerator e = o2.Items.GetEnumerator();
e.MoveNext();
OrderItem item3 = e.Current as OrderItem;
o2.RemoveItem(item3);
o2.Update();
item3.Delete();
Order o3 = new Order(o.OrderId);
Assert.AreEqual(o3.Items.Count, 2, "remove item fail! ");
o3.Delete();
} finally {
if (p != null && p.ProductId > 0) p.Delete();
if (p2 != null && p2.ProductId > 0) p2.Delete();
}
}
}
} [Test] // 测试 OrderItem 对象的 CRUD 操作.
public void TestItemCRUD() { OrderItem item = new OrderItem(); item.OrderId = 1; item.ProductId = 1; item.UnitPrice = 10.5M; item.Quantity = 12; item.Discount = 1; item.Create();
OrderItem item2 = new OrderItem(item.ItemId); item2.Quantity = 13; item2.Update();
OrderItem item3 = new OrderItem(item.ItemId); Assert.AreEqual(item3.Quantity, 13, "update orderitem fail! ");
item3.Delete();
}
}
// Order
这个太长了,不贴了,自己看代码吧.
// Order 映射文件
[Test] // 测试Product对象的CRUD操作. public void TestCRUD()
{
Category c = null;
try {
c = new Category();
c.CategoryName = "test";
c.Create();
Product p = new Product();
p.ProductName = "test";
p.Category = c;
p.SupplierId = 3;
p.QuantityPerUnit = "1箱10只";
p.UnitPrice = 10.5M;
p.Create();
Product p2 = new Product(p.ProductId);
p2.UnitPrice = 15.8M;
p2.Update();
Product p3 = new Product(p.ProductId);
Assert.AreEqual(p3.UnitPrice, 15.8M, "update fail! ");
p3.Delete();
} finally {
if (c != null && c.CategoryId > 0) c.Delete();
}
}
xml version = "1.0"encoding = "utf-8" ? ><hibernate - mapping xmlns = "urn:nhibernate-mapping-2.0" > <class name = "NHibernateTest.Order, NHibernateTest"table = "Orders" > <id name = "OrderId"column = "orderId"type = "Int32"unsaved - value = "0" > <generator class = "identity" / >id > <property name = "OrderDate"column = "orderDate"type = "DateTime" / ><property name = "RequiredDate"column = "requiredDate"type = "DateTime" / ><property name = "ShippedDate"column = "shippedDate"type = "DateTime" / ><property name = "Freight"column = "freight"type = "Decimal" / ><property name = "ShipName"column = "shipName"type = "String" / ><property name = "ShipAddress"column = "shipAddress"type = "String" / ><property name = "ShipCity"column = "shipCity"type = "String" / ><property name = "ShipRegion"column = "shipRegion"type = "String" / ><property name = "ShipPostalCode"column = "shipPostalCode"type = "String" / ><property name = "ShipCountry"column = "shipCountry"type = "String" / ><property name = "CustomerId"column = "customerId"type = "String" / ><property name = "EmployeeId"column = "employeeId"type = "Int32" / ><property name = "ShipVia"column = "shipVia"type = "Int32" / ><bag name = "_Items"cascade = "all"inverse = "true" >
来源: http://lib.csdn.net/article/dotnet/35950