在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦和浪费时间的。NHibernate是一个面向.NET环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping,ORM)这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。
NHibernate不仅仅管理.NET类到数据库表的映射(包括.NET 数据类型到SQL数据类型的映射),还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和ADO.NET处理数据的时间。
NHibernate的目标主要是用于与数据持久化相关的编程任务,能够使开发人员从原来枯燥的SQL语句的编写中解放出来,解放出来的精力可以让开发人员投入到业务逻辑的实现上。对于以数据为中心的程序,开发人员往往是在数据库中使用存储过程来实现商业逻辑,这种情况下NHibernate可能不是最好的解决方案,但对于那些基于.NET,并且能够实现OO业务模型和商业逻辑的中间层应用,NHibernate是最有用的。NHibernate可以帮助用户消除或者包装那些针对特定厂商的SQL代码,并且帮用户把结果集从表格式的表示形式转换成一系列的对象。
1.NHibernate体系结构NHibernate体系结构如图1-51所示。它体现了NHibernate如何使用数据库和配置文件数据来为应用程序提供持久化服务(和持久化的对象)。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] SessionFactory(NHibernate.IsessionFactory):它是Session的工厂,是ConnectionProvider的客户。可以持有一个可选的(第二级)数据缓存,可以在进程级别或集群级别保存的可以在事物中重用的数据。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 会话(NHibernate.ISession):单线程,生命期较短的对象,代表应用程序和持久化层之间的一次对话。封装了一个ADO.NET连接,也是Transaction的工厂。保存有必需的(第一级)持久化对象的缓存,用于遍历对象图,或者通过标识符查找对象。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 持久化对象(Persistent)及其集合(Collections):生命期较短的单线程的对象,包含了持久化状态和商业功能。这些可能是普通的对象,唯一特别的是现在从属于且仅从属于一个Session。一旦Session被关闭,它们都将从Session中取消联系,可以在任何程序层自由使用(比如,直接作为传送到表现层的DTO,数据传输对象)。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 临时对象(Transient Object)及其集合(Collection):目前没有从属于一个Session的持久化类的实例。这些可能是刚刚被程序实例化,还没有来得及被持久化,或者是被一个已经关闭的Session实例化。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 事务Transaction (NHibernate.ITransaction):(可选)单线程,生命期较短的对象,应用程序用其来表示一批工作的原子操作,它是底层的ADO.NET事务的抽象。一个Session在某些情况下可能跨越多个Transaction事务。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] ConnectionProvider(NHibernate.Connection.ConnectionProvider):(可选)ADO.NET连接的工厂。从底层的IDbConnection抽象而来。对应用程序不可见,但可以被开发者扩展/实现。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] TransactionFactory(net.sf.hibernate.TransactionFactory):(可选)事务实例的工厂。对应用程序不可见,但可以被开发者扩展/实现。
2.持久化类持久化类是应用程序用来解决商业问题的类(比如,在电子交易程序中的Customer和Order)。持久化类是暂时存在的,实例会被持久性保存于数据库中。
如果这些类符合简单的规则,NHibernate能够工作得最好,这些规则就是Plain Old CLR Object(POCO,简单传统CLR对象)编程模型。
POCO简单示例(用一个类描述一只猫)如下:
#001 public class Cat
#002 {
#003 private long _id; // 标识符
#004 private string _name; // 名字
#005 private DateTime _birthdate; // 出生日期
#006 private Cat _mate; // 猫的配对
#007 private Set _kittens; // 猫仔
#008 private Color _color; // 颜色
#009 private char _sex; // 性别
#010 private float _weight; // 体重
#011 public long Id // 标识符属性
#012 { get { return _id; }
#013 set { _id = value; }
#014 public string Name // 名字属性
#015 { get { return _name; }
#016 set { _name = value; }}
#017 public DateTime Birthdate // 出生日期属性
#018 { get { return _birthdate; }
#019 set { _birthdate = value; }}
#020 public Cat Mate // 猫的配对属性
#021 { get { return _mate; }
#022 set { _mate = value; }}
#023 public Set Kittens // 猫仔属性
#024 { get { return _kittens; }
#025 set { _kittens = value; }}
#026 public Color Color // 颜色属性
#027 { get { return _color; }
#028 set { _color = value; }}
#029 public char Sex // 性别属性
#030 { get { return _sex; }
#031 set { _sex = value; }}
#032 public float Weight // 体重属性
#033 { get { return _weight; }
#034 set { _weight = value; }}
#035 }
有4条主要的规则如下。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 为持久化字段声明访问器(getters和setters)。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] Cat为它的所有可持久化字段声明了getters和setters访问器。用访问器来替代直接访问字段是个好习惯。也可以通过字段(field)来使用NHibernate。属性不一定需要声明为public的。NHibernate可以对default、protected、internal或private的属性执行持久化。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 实现一个默认的构造方法(constructor):Cat有一个显式的无参数默认构造方法。所有的持久化类都必须具有一个默认的构造方法(可以不是public的),这样NHibernate就可以使用Constructor.Invoke()来实例化它们。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 提供一个标识属性(identifier property)(可选)。
[img,11,11]file:///C:/Documents%20and%20Settings/FAST/My%20Documents/1_5%20企业开发框架NHibernate和Spring_Net%20-%20《ASP_NET%202_0+SQL%20SERVER热门网络应用开发详解》%20-%20免费试读%20-%20book_csdn_net.files/image004.jpg[/img] 建议使用不是sealed的类(可选)。
3.NHibernate映射对象和关系数据库之间的映射是用一个XML文档(XML document)来定义的。这个映射文档被设计为易读的,并且可以手工修改。映射语言是以.NET为中心的,意味着映射是按照持久化类的定义来创建的,而非表的定义。在Hibernate中XML映射文档可以手动定义,也有一些工具来辅助生成,包括Xdoclet、Middlegen和AndroMDA,但是在NHibernate文档中并没有上述的辅助工具,不过可以采用MyGeneration这样的代码生成工具来生成XML配置文档。下面是一个映射的例子:
#001 <?xml version="1.0" ?>
#002 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.0" namespace="Eg" assembly="Eg">
#003 <class name="Cat" table="CATS" discriminator-value="C">
#004 <id name="Id" column="uid" type="Int64">
#005 <generator class="hilo"/>
#006 </id>
#007 <discriminator column="subclass" type="Char"/>
#008 <property name="Name" type="Char"/>
#009 <property name="Color" not-null="false"/>
#010 <property name="Sex" not-null="false" update="false"/>
#011 <property name="Weight"/>
#012 <many-to-one name="Mate" column="mate_id"/>
#013 <set name="Kittens">
#014 <key column="mother_id"/>
#015 <one-to-many class="Cat"/>
#016 </set>
#017 <subclass name="DomesticCat" discriminator-value="D">
#018 <property name="Name" type="String"/>
#019 </subclass>
#020 </class>
#021 </hibernate-mapping>
所有的XML映射都需要使用nhibernate-mapping-2.0 schema。目前的schema可以在NHibernate的资源路径或者是NHibernate.dll的嵌入资源(Embedded Resource)中找到。NHibernate总是会优先使用嵌入在资源中的schema文件。在使用VisualStudio.NET时,用户应该将hibernate-mapping拷贝到C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\ Packages\schemas\xml路径中,以获得智能感知功能。
#002行:hibernate-mapping这个元素包括4个可选的属性。schema属性,指明了这个映射所引用的表所在的schema名称。假若指定了这个属性,表名会加上所指定的schema的名字扩展为全限定名。假若没有指定,表名就不会使用全限定名。default-cascade指定了未明确注明cascade属性的属性和集合类会采取什么样的默认级联风格。auto-import属性默认在查询语言中可以使用非全限定名的类名。default-access告诉用户怎么访问属性值。假若有两个持久化类,它们的非全限定名是一样的,应该设置auto-import= "false"。假若把一个“import”过的名字同时对应两个类,NHibernate会抛出一个异常。
#003行:使用class元素来定义一个持久化类。name是持久化类(或者接口)的全限定名。table是对应的数据库表名。discriminator-value(可选 - 默认和类名一样)是一个用于区分不同的子类的值,在有多态行为时使用。
#004行:使用id来标识主键字段,因为被映射的类必须声明对应数据库表主键字段。大多数类有一个属性,为每一个实例包含唯一的标识。<id> 元素定义了该属性到数据库表主键字段的映射。
#005行:必须声明的 <generator> 子元素是一个.NET类的名字,用来为该持久化类的实例生成唯一的标识。其中hilo(高低位)使用一个高/低位算法来高效地生成Int64、Int32 或者Int16类型的标识符。给定一个表和字段(默认分别是hibernate_unique_key 和next)作为高位值的来源。高/低位算法生成的标识符只在特定的数据库中是唯一的。
#007行:在“一棵对象继承树对应一个表”的策略中,<discriminator>元素是必需的,它声明了表的识别器字段。识别器字段包含标志值,用于告知持久化层应该为某个特定的行创建哪一个子类的实例。只能使用如下受到限制的类型:String,Char,Int32,Byte,Int16,Boolean,YesNo,TrueFalse。
#008-011行:<property>元素为类声明了一个持久化的属性。name是属性的名字,type(可选)是一个NHibernate类型的名字,not-null(可选)表明这个属性是否为空,update(可选-默认为true)表明在用于UPDATE的SQL语句中是否包含这个字段。
#012行:通过many-to-one元素,可以定义一种常见的与另一个持久化类的关联。这种关系模型是多对一关联(实际上是一个对象引用)。
#013-016行:<set>为集合类标识,其中discriminator-value(可选 - 默认为类名)用于区分每个独立的子类的值。
#017行:子类(subclass),多态持久化需要为父类的每个子类都进行声明。对于上面建议的“每一棵类继承树对应一个表”的策略来说,就需要使用<subclass>声明。
4.NHibernate应用实例上面已经编写好了Cat类和相应的映射文件,为了正确调用Nhibernate,必须对web.config配置文件进行配置。具体配置代码如下:
#001 <xml version="1.0" encoding="utf-8" ?>
#002 <configuration>
#003 <configSections>
#004 <section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System Version=2.0.0.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
#005 </configSections>
#006 <nhibernate>
#007 <add
#008 key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider" />
#009 <add
#010 key="hibernate.connection.driver_class"
value="NHibernate.Driver.SqlClientDriver" />
#011 <add
#012 key="hibernate.connection.connection_string"
value="server=localhost;uid=sa;pwd=123;database=Sample" />
#013 <add
#014 key="hibernate.connection.isolation" value="ReadCommitted" />
#015 <add
#016 key="hibernate.dialect" value="NHibernate.Dialect.MsSql2000Dialect" />
#017 </nhibernate>
#018 </configuration>
#012行:hibernate.connection.connection_string的value为连接服务器ConnectoinString。其他的都是Nhibernate运行所必需的配置信息。
配置完web.config文件,在测试工程中引用NHibernate.dll和Eg,具体代码如下。
#001 using System;
#002 using System.Collections.Generic;
#003 using System.Text;
#004 using NHibernate;
#005 using NHibernate.Cfg;
#006 using POJOLibrary;
#007 namespace Test
#008 {
#009 class Program
#010 {
#011 static void Main(string[] args)
#012 { Configuration c = new Configuration();
#013 c.AddAssembly("Eg");
#014 ISessionFactory factory = c.BuildSessionFactory();
#015 ISession s = factory.OpenSession();
#016 ITransaction t = s.BeginTransaction();
#017 Cat cat = new Cat();
#018 cat.Name = “Bolo”;
#019 cat.Color = “Black”;
#020 cat.Sex = “Male” ;
#021 cat.Weight = “12” ;
#022 s.Save(cat);
#023 t.Commit();}
#024 }
#025 }
NHibernate是通过Isession的实现类来提供功能的,上面代码通过Itransaction打开一个事务t=s.BeginTransaction(),最后通过调用t.Commit()提交事务,将数据写入数据库中。
通过上述NHibernate的一个简单的应用实例可以看到,整个应用程序中没有写任何一句SQL语句。
虽然NHibernate目前不是非常的成熟,还没有达到实用化的水平,它经常在负荷有些大的时候崩溃,但是它脱胎于在Java社区有着巨大影响力的Hibernate,而且NHibernate在实际应用方面确实体现出了它的优势,基于上述原因NHibernate一定会被越来越多的开发人员所接受,而且一定会被标准化。到目前为止,NHibernate的最新版本为1.1,官方站点http://nhibernate.sourceforge.net/上目前的版本是beta 0.7.0.0。