博客园有不少优秀的NHibernate起步的教程,但很多的都是官方StartKit的教程,关于如何来解决初学者所遇到的问题上,以及对NHibernate使用中一些关键点如何理解上所述甚少。本文是我自己在学习NHiberNate中所遇到的一些关键点以及一些问题的记录。希望对我或与我有相同困惑的人有所帮助。
博客园有不少优秀的NHibernate起步的教程,但很多的都是官方StartKit的教程,关于如何来解决初学者所遇到的问题上,以及对NHibernate使用中一些关键点如何理解上所述甚少。本文是我自己在学习NHiberNate中所遇到的一些关键点以及一些问题的记录。希望对我或与我有相同困惑的人有所帮助。
写了一个示例程序,来实现Many2One,示例程序组成:Order与OrderDetail,对应的Hbm与cs文件如下:
数据库如下:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
表结构
USE [NHibernate]
GO
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//****** 对象: Table [dbo].[TBL_ORDER] 脚本日期: 07/18/2007 15:39:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBL_ORDER](
[ID] [uniqueidentifier] NOT NULL,
[USERNAME] [nvarchar](40) COLLATE Chinese_PRC_CI_AS NOT NULL,
[ORDERTIME] [datetime] NOT NULL,
[TOTALAMOUNT] [decimal](18, 2) NOT NULL CONSTRAINT [DF_TBL_ORDER_TOTALCOUNT] DEFAULT ((0)),
CONSTRAINT [PK_TBL_ORDER] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
USE [NHibernate]
GO
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//****** 对象: Table [dbo].[TBL_ORDER_DETAIL] 脚本日期: 07/18/2007 15:40:39 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[TBL_ORDER_DETAIL](
[ID] [uniqueidentifier] NOT NULL,
[ORDER_ID] [uniqueidentifier] NOT NULL,
[ITEM_NAME] [nvarchar](40) COLLATE Chinese_PRC_CI_AS NOT NULL,
[AMOUNT] [decimal](18, 2) NOT NULL CONSTRAINT [DF_TBL_ORDER_DETAIL_AMOUNT] DEFAULT ((0)),
CONSTRAINT [PK_TBL_ORDER_DETAIL] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
GO
USE [NHibernate]
GO
ALTER TABLE [dbo].[TBL_ORDER_DETAIL] WITH CHECK ADD CONSTRAINT [FK_TBL_ORDER_DETAIL_TBL_ORDER] FOREIGN KEY([ORDER_ID])
REFERENCES [dbo].[TBL_ORDER] ([ID])
Order.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<class name="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" table="TBL_ORDER"
lazy="false">
<id name="Id" column="ID" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid" />
</id>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<property name="UserName" column="USERNAME" type="String" length="40"/>
<property name="OrderTime" column="ORDERTIME" type="DateTime"></property>
<property name="TotalAmount" column="TOTALAMOUNT" type="Decimal"/>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<bag name="Items" lazy="true" inverse="true" cascade="all-delete-orphan">
<key column="ORDER_ID"></key>
<one-to-many class="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain"/>
</bag>
</class>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
</hibernate-mapping>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
OrderDetail.hbm.xml
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<class name="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain" table="TBL_ORDER_DETAIL"
lazy="false">
<id name="Id" column="ID" type="Guid"
unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid" />
</id>
<many-to-one name="Order" class ="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" column="ORDER_ID" not-null="true"/>
<property name="ItemName" column="ITEM_NAME" type="String" length="40"/>
<property name="Amount" column="AMOUNT" type="Decimal"/>
</class>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
</hibernate-mapping>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Order.cs
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Text;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace NHibernateStudy.Domain
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class Order
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Fields#region Fields
private Guid id;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private string username;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private DateTime ordertime;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private Decimal totalamount;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private IList<OrderDetail> itmes;
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Properties#region Properties
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual Guid Id
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return id; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ id = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual string UserName
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.username; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.username = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual DateTime OrderTime
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.ordertime; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.ordertime = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual Decimal TotalAmount
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.totalamount; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.totalamount = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual IList<OrderDetail> Items
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.itmes; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.itmes = value; }
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 构造函数。条目信息用ArrayList来保存
/// </summary>
public Order()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.itmes = new List<OrderDetail>() ;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// 增加条目信息
/// </summary>
/// <param name="detail">Detail类的实例</param>
public void AddDetail(OrderDetail detail)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
detail.Order = this;
this.itmes.Add( detail );
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void CaculateTotal()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
foreach (OrderDetail detail in this.itmes)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.totalamount += detail.Amount;
}
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
OrderDetail.cs
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Text;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace NHibernateStudy.Domain
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class OrderDetail
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Field#region Field
private Guid id;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private Order order;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private string itemName;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private Decimal amount;
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Properties#region Properties
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual Guid Id
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return id; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ id = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual Order Order
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return order; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.order = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual string ItemName
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.itemName; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.itemName = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual Decimal Amount
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.amount; }
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.amount = value; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
在写程序中共遇到如下的疑惑处,均已经解决。现记录如下
第一点:写Hbm中遇到的问题
实现Many2One有两种实现方法,一种是只在父类的Hbm中增加信息,不改变子表的Hbm的信息。但这种方法有如下缺点
1) 子表中父表的关联字段即FK不可为空
2) 在每次更新子表的时候都是分两步走的Insert然后Update,存在效率问题,且第一次Insert的时候FK为空。
由于存在这两个方面的问题,所以一般不采用这种方法。关于上面两个缺陷请看下文(下文来自NHiberNate的自带文档)
Suppose we start with a simple <one-to-many> association from Parent to Child.
<set name="Children">
<key column="parent_id" />
<one-to-many class="Child" />
</set>
If we were to execute the following code
Parent p =
..;
Child c = new Child();
p.Children.Add(c);
session.Save(c);
session.Flush();
NHibernate would issue two SQL statements:
an INSERT to create the record for c
an UPDATE to create the link from p to c
This is not only inefficient, but also violates any NOT NULL constraint on the parent_id column.
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
所以一般采用第二种方法,我自己写的示例程序也是采用这种方法的。
首先看条目类的HBM文件,在子类的hbm文件加入:
<many-to-one name="Order" class ="NHibernateStudy.Domain.Order, NHibernateStudy.Domain" column="ORDER_ID" not-null="true"/>
备注:
1) 条目类的Hbm文件用many-to-one来与主类相联系
2) name属性,表示在条目类中,主类的Properties名称
3) class属性,表示主类的类名,与名称空间
4) column属性,表示在条目类对应的条目表中与主类对应的父表关联字段。简单点说就是条目表中对应主表的那个外键
5) not-null属性,表示是否为空。
看主类的HBM文件,在主类的Hbm文件加入
<bag name="Items" lazy="true" inverse="true" cascade="all-delete-orphan">
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<key column="ORDER_ID"></key>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<one-to-many class="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain"/>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
</bag>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
关于用哪种表示集合,见下面的(http://wljcan.cnblogs.com/archive/2004/06/30/19713.html)
在Nhibernate中经常遇到one-to-many和many-to-many的关系映射,用一些集合类来保存关联的many集合,这些集合类包括: IList、Array和IDictionary。其在map文件中对应的元素为 list(IList)、set(IDictionary)、bag(IList)、map(IDictionary)和array(Array)。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
其中比较特殊的是List和Array,它们需要在关联表中用一个单独字段来保存列表(List)或数组(Array)的索引(如childRecord[i]中的i),虽然list可以实现对无序元素的访问,但是在nhibernate中还是必须要提供索引。这样就出现一个问题:如果表中没有这样的index字段,将无法使用array,这样可能降低性能,因为使用IList的时候需要 boxing 和unboxing。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
另外,在使用的过程中发现使用 list无法与datagrid绑定,而bag却可以。
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<bag name="Students" lazy="true" inverse="true" >
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<key column="TeacherID"/>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<one-to-many class="testMSSql.student, testMSSql" />
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
</bag>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<list name="Students" lazy="true" inverse="true" >
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<key column="TeacherID"/>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<index column="id" />
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
<one-to-many class="testMSSql.student, testMSSql" />
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
</list>
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
备注:1)<bag>表示集合,集合的区别见上
2)lazy = true,表示延迟加载。经试验,此处的lazy与下面的lazy是有区别的
<class name="NHibernateStudy.Domain.OrderDetail, NHibernateStudy.Domain" table="TBL_ORDER_DETAIL" lazy="false">
如果在类中写 lazy= true,那么此类的所有属性均要为 public virtual。而在关联时,写上lazy=true,则无此要求。但按照建议,还是将类的所有属性写成virtual要好。
3) inverse = true,表示将主类与条目类之间的关系控制移交给条目类来完成。即
Parent p = (Parent) session.Load(typeof(Parent), pid);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Child c = new Child();
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
c.Parent = p;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
p.Children.Add(c);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
session.Save(c);
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
session.Flush();
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
必须要写 c.Parent = p;来完成主类与条目类之间的关系,没有此语句,仅仅将条目类放入主类中的语句p.Children.Add(c),无法完成主类与条目类的关联。
为了简化这个过程,可以在主类中加入如下函数
public void AddChild(Child c)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
c.Parent = this;
children.Add(c);
}
Now, the code to add a Child looks like
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
Parent p = (Parent) session.Load(typeof(Parent), pid);
Child c = new Child();
p.AddChild(c);
session.Save(c);
session.Flush();
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4) cascade表示级联使用的场合
5) 属性 Key column表示主表与条目表的关联字段。记住是这个关联字段在条目表中的名称。即无论是bag中还是条目类的hbm文件中的many-to-one中所指定的column都是指条目表中外键字段的名称。
6) 属性one-to-many,指定条目类
第二点: 写程序中遇到的问题
1) 在条目类中放入一个引用的主类的变量。变量名称即为在条目类中many-to-one中name属性指定的值。刚开始由于还是以数据库的思维考虑,放入了一个主类的ID,汗一个。
2) 父类中放一个条目类的IList。在父类中使用范型的ILIst来完成条目类的引用效率更高,相关代码如下:
using System.Collections.Generic;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
private IList<OrderDetail> itmes;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
public virtual IList<OrderDetail> Items
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return this.itmes; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ this.itmes = value; }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3) 在父类的构造函数中要建立子类所用的IList实例。刚开始,忘记new此实例导致在加入条目类的时候出现空引用。代码如下:
public Order()
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
this.itmes = new List<OrderDetail>() ;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3) 编译时遇到的问题
1) 是遇到空引用的问题,问题源自2-3
出现了 mappingException,找不到对应的EntityClass。――未将对应的hbm文件作为嵌入资源。即设置文件*.hbm.xml的生成操作属性为“嵌入的资源”。感谢(http://www.cnblogs.com/david-chan/archive/2006/02/18/333230.aspx)