NH的online document中讨论了三种情况的的o/r mapping:
1)one-to-one
2)one-to-many / many-to-one
3)many-to-many
因为官方文档介绍得很少,学起来非常费劲,我在这里做一个学习总结,希望能引起大家的继续讨论。
为了便于描述。,本系列学习手记将引入Category和Item对象,分别实现以下关系:
1)Category和Item对象之前不存在关系(none-association);
2)Category和Item对象之前存在着one-to-many的关系,即一个Category对象对应多个Item对象;
3)Category和Item对象之间存在着many-to-many的关系;
4)Category和Item对象之间存在着one-to-one的关系(我认为这是最少用到的关系类型)。
本文将以Category对象的简单操作来示例第一种情况。Category对象只有两个属性:ID(guid)和Name(string),我们来看看怎么使用NH来进行Category对象的CRUD操作。
主要内容
1、准备数据库
2、编写配置文件
3、编写POCO类
4、Category对象的CRUD操作
一、准备数据库
新建数据表,对应于Category对象的属性,该数据表只有CategoryID和Name两个字段:
CREATE
TABLE
[
dbo
]
.
[
nh_categories
]
(
[
CategoryID
]
[
uniqueidentifier
]
NOT
NULL
,
[
Name
]
[
nvarchar
]
(
50
) COLLATE Chinese_PRC_CI_AS
NOT
NULL
)
ON
[
PRIMARY
]
GO
二、编写配置文件
nh最令我不满的一点是即使是尝试一个非常简单的crud操作,都要先编写配置文件,而且目前好像也没有很好的自动生成工具。下面让我们新建Console工程BasicMappings,编写nhibernate配置文件和xml mappings文件。
1、新建文件hibernate.cfg.xml,并输入一下内容:
<?
xml version="1.0" encoding="utf-8"
?>
<
hibernate-configuration
xmlns
="urn:nhibernate-configuration-2.0"
>
<
session-factory
name
="CollectionMappings"
>
<!--
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
>
其中配置节的解释可以参考我之前发的
NHibernate学习手记(3) - NH的配置信息,其中的show_sql表示是否在Console输出nh进行数据操作生成的sql语句。
同时,因为hibernate.cfg.xml文件要求在输出目录下,还需要在[项目属性]->[通用属性]->[生成事件]->[生成后事件命令行]中添加
copy $(ProjectDir)"hibernate.cfg.xml $(TargetDir)
用于在生成exe时把hibernate.cfg.xml文件拷贝到输出目录中。
2、新建objects.hbm.xml文件,并把文件属性设为”嵌入资源“。NH将根据*.hbm.xml中的配置进行o/r mapping的操作。
输入以下内容:
<?
xml version="1.0" encoding="utf-8"
?>
<
hibernate-mapping
xmlns
="urn:nhibernate-mapping-2.0"
>
<!--
mapping for Category
-->
<
class
name
="BasicMappings.Category, BasicMappings"
table
="nh_categories"
>
<
id
name
="CategoryID"
column
="CategoryID"
type
="Guid"
>
<
generator
class
="guid"
/>
</
id
>
<
property
name
="Name"
type
="string"
length
="50"
/>
</
class
>
</
hibernate-mapping
>

把NH源文件中包含的nhibernate-mapping-2.0.xsd文件拷贝到%.NET 2003安装目录%"Common7"Packages"schemas"xml文件夹下,可获得intellisense的输入支持。
1) class节点。该节点指示NH对该类的对象进行orm操作的所需的信息。
class节点的部分重要属性含义:
Attributes |
Usage |
Example |
name |
指示所映射类的全限定名称,格式如namespace.className.assemblyName。Required |
BasicMappings.Category, BasicMappings |
table |
指示该类所对应的数据表名称。Required |
nh_categories |
schema |
指示所使用的数据库schema,默认继承hibernate-mappings的schema设定。 |
NhTrial.dbo |
mutable |
指示该类的对象可变,即nh是否可对此对象进行保存和修改的持久化操作。Optional |
|
其他还有lazy, persister和proxy等可选属性,在后续文章中会有介绍。
2) id节点。id节点设定了该class的主键信息:
Attributes |
Usage |
Example |
name |
指示对象的主键属性的名称 |
ID |
type |
指示该属性的NH数据类型。可选,NH可自动转换。 |
Guid |
column |
指示该属性对应的数据字段名称,默认取name属性值。Optional |
ID |
unsaved-value |
指示该对象未保存的主键属性的取值,用于ISession.SaveOrUpdate()操作提供根据。Optional |
|
access |
指示NH对该属性所采取的access-strategy和naming-strategy。默认从hibernate-mapping的default-access继承。Optional |
field.camelcase |
3)generator节点。该节点指示了主键的生成方式
class |
usage |
identity |
支持DB2/MySql/MsSql/Sybase/HypersonicSql,生成整型自增id |
sequence |
支持DB2/PostgreSql/Oracle,生成整型自增id |
guid |
指示使用Guid.NewGuid来生成主键值 |
native |
指示根据数据类型,按照identity、sequence或hilo的方式生成主键 |
assigned |
指示主键值的设定由用户代码完成,NH无需理会 |
4)property节点。该节点指示了NH进行属性映射所需要的信息:
Attributes |
Usage |
Example |
name |
指示对象的属性名称 |
Name |
column |
指示该属性对应的数据列名称,默认取name属性。Optional |
Name |
type |
指示该属性的NH数据类型。可选,NH可自动转换 |
String |
update |
指示在进行update时是否保存该属性的设置。 |
true|false |
insert |
指示在进行save操作时是否保存该属性的设置。 |
true | false |
formula |
指示该属性为表达式,将使column设置失效。 |
ID + Name |
access |
指示NH对该属性所采取的access-strategy和naming-strategy。默认从hibernate-mapping的default-access继承。Optional |
field.camelcase |
解释一下反复出现access设置,access=access-strategy + . + naming-strategy。
access-trategy的取值包括:
1)Property:默认值,NH在进行orm时将使用已定义的getter和setter来进行该属性的读取和设置。
2)field:NH在进行orm时,将使用反射来读取和设置数据成员。
3)nosetter:使用getter来读取属性值,使用反射方式来设置对应的数据成员。
4) ClassName:使用指定的访问类进行属性的访问和设置,ClassName为该访问类的全限定名称。
naming -strategy指示了在映射时,应该对name属性进行格式转换的方式。除非access-strategy为nosetter,naming- strategy为可选设置,当未设置naming-strategy时,将直接使用name属性值进行映射。下面以name=FooBar为例,看看 naming-strategy的转换后的结果:
naming-strategy |
example |
未设置 |
FooBar |
camelcase |
fooBar |
camelcase-underscore |
_fooBar |
lowercase |
Foobar |
lowercase-underscore |
_foobar |
pascalcase-underscore |
FooBar |
pascalcase-m-underscore |
_FooBar |
|
|

除了上述编写xml mappings文件的方式外,NHibernateContrib库中还有通过Attribute的方式来设定orm信息的方式,在后续文章中会有介绍。
三、编写POCO类
POCO类的说明可参考我之前发的
NHibernate学习手记(4) - 持久化类(Persistent class)的设计。
新建类Category.cs,内容如下:
1
using
System;
2
3
namespace
BasicMappings
4
{
5
///
<summary>
6
///
Category 的摘要说明
7
///
</summary>
8
///
创 建 人: Aero
9
///
创建日期: 2006-3-17
10
///
修 改 人:
11
///
修改日期:
12
///
修改内容:
13
///
版 本:
14
public
class
Category
15
{
16
private
Guid _categoryId;
17
18
private
string
_name
=
string
.Empty;
19
20
public
Guid CategoryID
21
{
22
get
{
return
this
._categoryId; }
23
set
{
this
._categoryId
=
value; }
24
}
25
26
public
string
Name
27
{
28
get
{
return
this
._name; }
29
set
{
this
._name
=
value; }
30
}
31
32
#region
构造函数
33
///
<summary>
34
///
默认无参构造函数
35
///
</summary>
36
///
创 建 人: Aero
37
///
创建日期: 2006-3-17
38
///
修 改 人:
39
///
修改日期:
40
///
修改内容:
41
public
Category()
42
{
43
44
}
45
46
#endregion
47
}
48
}
49
四、Category对象的CRUD操作
本文将演示如何保存随机生成的Category对象,和如何实现对象的查询。简单的CRUD操作可参考
NHibernate学习手记(1) - 对象的简单CRUD操作 。新建文件Programm.cs:
1、保存Category对象
1
static
readonly
ISessionFactory _factory;
2
3
static
ISessionFactory Factory
4
{
5
get
{
return
Programm._factory; }
6
}
7
8
///
<summary>
9
///
initializing
10
///
</summary>
11
static
Programm()
12
{
13
Configuration cfg
=
new
Configuration().Configure();
14
cfg.AddAssembly(
"
BasicMappings
"
);
15
16
//
initialize factory
17
Programm._factory
=
cfg.BuildSessionFactory();
18
}
19
20
///
<summary>
21
///
save transient object
22
///
</summary>
23
///
<param name="persistentObj"></param>
24
static
void
Save(
object
persistentObj)
25
{
26
using
(ISession session
=
Programm.Factory.OpenSession())
27
{
28
ITransaction trans
=
session.BeginTransaction();
29
30
try
31
{
32
session.Save(persistentObj);
33
trans.Commit();
34
}
35
catch
36
{
37
trans.Rollback();
38
throw
;
39
}
40
}
41
}
1)line13-18,初始化Configuration和SessionFactory。line13中的
Configuration cfg
=
new
Configuration().Configure();
将根据hibernate.cfg.xml文件的配置创建Congfiguration对象,但它并不知道任何orm信息。我们可以通过以下几种方式给Configuration对象提供o/r mapping的信息:
A、通过Configuration.AddAssembly(assemblyName),nh将自动查找所制定程序集中的所有*.hbm.xml嵌入文件获取o/r mapping信息
cfg.AddAssembly(
"
BasicMappings
"
);
B、通过Configuration.AddType(Type t),nh将自动查找当前程序集中名为typeName.hbm.xml的嵌入文件该类的o/r mapping信息
cfg.AddClass(
typeof
(Category));
C、通过Configuration.AddXmlFile(fileName),nh将自动查找当前程序集中名为fileName的嵌入文件,获取o/r mapping信息
cfg.AddXmlFile(
"
objects.hbm.xml
"
);
2)line24-41,保存对象。NH中进行对象的save/update/delete操作时都必须打开事务,推荐使用using的方式来使用ISession对象。
2、查询全部对象
1
///
<summary>
2
///
list all category objects in the repository
3
///
</summary>
4
static
void
ListCategory()
5
{
6
using
(ISession session
=
Programm.Factory.OpenSession())
7
{
8
IList categories
=
session.CreateCriteria(
typeof
(Category)).List();
9
10
foreach
(Category category
in
categories)
11
{
12
Console.WriteLine(category.CategoryID
+
"
"
+
category.Name);
13
}
14
}
15
}
3、删除全部对象
作为一种好习惯,我们在示例代码的开始前和结束后都应该清空所添加的临时数据:)
1
///
<summary>
2
///
remove all category objects from the repository
3
///
</summary>
4
static
void
Clear()
5
{
6
using
(ISession session
=
Programm.Factory.OpenSession())
7
{
8
ITransaction trans
=
session.BeginTransaction();
9
10
try
11
{
12
session.Delete(
"
from Category
"
);
13
trans.Commit();
14
}
15
catch
16
{
17
trans.Rollback();
18
throw
;
19
}
20
}
21
}
在删除所有Category对象时,我们用了一小段HQL代码“from Category”,在后续的文章里会有介绍。
完整的代码可从下载:
ObjectsMapping.rar,其中的ExtendedCategory示例了更多的ISession的操作。