[翻译]NHibernate1.2.0文档学习(1)-QuickStart例子
序言
在如今的企业环境中,面向对象软件和一个关系型数据库一起工作是麻烦的和耗费时间的。Nhibernate是一个在.NET平台下的ORM工具。这个ORM团队描述说这是一个转换对象模型的数据到一个基于SQL架构的关系型数据模型的映射数据方法。
NHibernate不但关注.NET类到数据库表的映射(也可以说是.NET数据类型到SQL数据类型),同时提供了数据查询和检索工具。更重要的是它比使用SQL和ADO.NET进行手工处理减少了开发时间。
NHibernate的目标是让开发者从通常的数据持久化相关的编程工作中释放出来。对于那些以数据为中心,在数据库中只以存储过程去实现业务逻辑的应用程序,NHibernate并不是最好的,但是在基于.NET的中间层中的面向对象领域模型和业务逻辑是非常有用的。NHibernate能帮助你移除或封装指定的SQL代码,同时在通常的操作下,将数据中列表式转化为对象。
使用IIS和Microsoft SQL Server快速起步
1.1 开始NHibernate
这个教程描述了在Microsoft环境下尽心设置NHibernate 1.0.2。教程中使用的工具如下:
Microsoft Internet Information Services(IIS) – web服务器支持ASP.NET。
Microsoft SQL Server 2000 – 数据库服务器。本教程使用桌面版(MSDE),微软提供的一个免费版本。它也支持其它数据库,只需改变NHibernate SQL 方言和驱动配置。
Micorsoft Visual Studio .NET 2003 – 开发环境。
首先,我们创建一个新的web工程。我们取名QuickStart,该工程的虚拟目录为http://localhost/QuickStart。在这个工程中,添加NHibernate.dll。Visual Studio将自动复制类库和它的依赖项到该工程的输出目录。如果你使用一个不同与SQL Server的数据库,在该工程的驱动程序中添加一个引用。
我们现在设置使用NHibernate进行数据库连接的配置信息。首先,打开该工程自动产生的wb.config文件,把下面的配置信息添加到文件中。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- Add this element -->
<configSections>
<section
name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"
/>
</configSections>
<!-- Add this element -->
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.connection_string">Server=(local);initial catalog=quickstart;Integrated Security=SSPI</property>
<mapping assembly="QuickStart" />
</session-factory>
</hibernate-configuration>
<!-- Leave the system.web section unchanged -->
<system.web>
...
</system.web>
</configuration>
元素<configSections>包含了项的定义信息,后续处理者使用它来处理它们的内容。我们申明了一个<hibernate-configuration>,它通知Nhibernate程序使用Microsoft SQL Server 2000数据库和使用指定的连接串连接到数据库。方言(dialect)是必须设置的,因为不同的数据库对SQL语句的解释是不同的。Nhibernate关注这些不同点,并针对不同的主流商业数据库和开源数据库设定了不同的方言。
在Nhibernate中,一个ISessionFactory代表了单个数据库。如果你想在你的应用程序中使用多个数据库,你可以创建多个XML配置文件,创建多个配置项和ISessionFactory对象。
<hibernate-configuration>中最后申明的QuickStart做为包含类描述和映射文件的程序集的名称。这个映射文件包含了从POCO类到对应的一个数据库表(或多个表)的映射信息。接下来,让我们先来看看POCO类和对应到这个类的映射文件。
1.2 第一个持久类
在使用Nhibernate时,最好使用简单的CLR对象(POCOs,Plain Old CLR Objects)作为持久类。一个POCO类通过使用.NET的标准的属性机制,可以实现数据访问性控制,在公开接口的情况下,又能隐藏内部数据。
namespace QuickStart
{
public class Cat
{
private string id;
private string name;
private char sex;
private float weight;
public Cat()
{
}
public virtual string Id
{
get { return id; }
set { id = value; }
}
public virtual string Name
{
get { return name; }
set { name = value; }
}
public virtual char Sex
{
get { return sex; }
set { sex = value; }
}
public virtual float Weight
{
get { return weight; }
set { weight = value; }
}
}
}
Nhibernate并没有限制属性的类型,所有的.NET类型,原始类型(比如string,char和DateTime)和来自System.Collections命名空间的类都可以做为映射的类型。你可以把它们映射为值,值的集合,或关联到其它实体。Id是一个特别的属性,它是这个类的数据库标识(主键),这是强烈推荐的。我们可以不在类中申明标识符,Nhibernate将使用内部的标识,但是这样我们的应用程序架构将失去一些灵活性。
我们不需要让持久化类去实现某个接口,或者继承自某个根持久化类。Nhibernate不在编译时处理,比如IL操作。它唯一依赖.NET反射和运行时类强化(通过Castle.DynamicProxy)。因此,通过Nhibernate,我们能把POCO类中的任何关系映射到数据表中。
为了让上面所说的运行时类强化实现工作,Nhibernate需要所有的实体类的工作属性声明为virtual。
1.3映射Cat
Cat.hbm.xml映射文件包含了对象关系映射所需的元数据。这个元数据包含了持久化类的申明和映射到数据库表的属性(列和关联到其它实体的外键)。
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="QuickStart" assembly="QuickStart">
<class name="Cat" table="Cat">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by NHibernate with the UUID pattern. -->
<id name="Id">
<column name="CatId" sql-type="char(32)" not-null="true"/>
<generator class="uuid.hex" />
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="Name">
<column name="Name" length="16" not-null="true" />
</property>
<property name="Sex" />
<property name="Weight" />
</class>
</hibernate-mapping>
每一个持久化类应该有一个标识特性(attribute)(实际上,只是类去表现实体,实体没有依赖值对象,值对象被映射为实体的一部分)。这个属性是用来区分持久化对象:如果CatA.Id.Equals(CatB.Id)返回true,那么这两个cat是相等的,这个被称为数据库标识。Nhibernate内置了多种针对不同场景的标识符产生器(包括产生数据库序列的native,产生表示表的hi/lo和应用程序分配标识符)。我们使用UUID产生器(只为测试目的,像数据库的整数代理键产生器)和指定表Cat中的列CatID为Nhibernate标识产生器产生的值(作为表的主键)。
Cat的所有其它属性是被映射到同一张表。我们在映射Name属性中使用显示的数据库列声明。当我们使用Nhibernate的SchemaExport工具产生映射文件,并根据这个映射文件自动产生数据库架构的情况是特别有用的。其它所有的属性使用默认值进行映射,它需要耗费你的多数时间。Cat数据库表结构如下:
Column | Type | Modifiers
--------+--------------+----------------------
CatId | char(32) | not null, primary key
Name | nvarchar(16) | not null
Sex | nchar(1) |
Weight | real |
你现在应该手工创建数据库及表,如果你想自动完成这一步,你可以使用SchemaExport工具,你可以读第十六章,工具集向导。这个工具可以创建一个完全的SQL DDL,包括表定义,自定义列类型约束,唯一性约束和索引。如果你使用SQL Server,你应该确保ASPNET用户有权限操作数据库。
1.4运行工程
我们现在开始使用Nhibernate的ISession。ISession是持久化对象管理接口,我们使用它存储Cats到数据库和从数据库中检索Cats。但是,我们必须先从ISessionFactory得到一个ISession(Nhibernate的工作单元):
ISessionFactory sessionFactory =
new Configuration().Configure().BuildSessionFactory();
一个ISessionFactory负责一个数据库,并且它只能使用一个XML配置文件(Web.config或hibernate.Cfg.xml)。在创建ISessionFactory(它是不可变的)之前,你可以通过访问Configuration类来设置其它属性(甚至改变映射元数据)。我们在那里创建ISessionFactory?在我们的应用程序中我们怎么样访问它?
一个ISessionFactory通常只创建一次,比如:启动时在Application_Start事件处理中。这意味着在ASP.NET页面中,你不应该保持它在实例变量中,而是应该保存在其它位置。并且,我们需要各种单件模式,这样我们能在应用程序代码中很容易的访问ISessionFactory。这个方法说明了下面两个问题的解决方法:configuration和更容易的访问ISessionFactory。
我们实现了一个NhibernateHelper帮助类:
using System;
using System.Web;
using NHibernate;
using NHibernate.Cfg;
namespace QuickStart
{
public sealed class NHibernateHelper
{
private const string CurrentSessionKey = "nhibernate.current_session";
private static readonly ISessionFactory sessionFactory;
static NHibernateHelper()
{
sessionFactory = new Configuration().Configure().BuildSessionFactory();
}
public static ISession GetCurrentSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CurrentSessionKey] as ISession;
if (currentSession == null)
{
currentSession = sessionFactory.OpenSession();
context.Items[CurrentSessionKey] = currentSession;
}
return currentSession;
}
public static void CloseSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items[CurrentSessionKey] as ISession;
if (currentSession == null)
{
// No current session
return;
}
currentSession.Close();
context.Items.Remove(CurrentSessionKey);
}
public static void CloseSessionFactory()
{
if (sessionFactory != null)
{
sessionFactory.Close();
}
}
}
}
这个类不但关注ISessionFactory,而且还保留了当前HTTP请求的ISession。
一个ISessionFactory是线程安全的,多个线程能并发的访问它和请求ISessions。一个ISession不是线程安全的,它代表了与数据库关联的单个工作单元。ISessions是通过ISessionFactory打开,并当所有工作结束后进行关闭。
ISession session = NHibernateHelper.GetCurrentSession();
ITransaction tx = session.BeginTransaction();
Cat princess = new Cat();
princess.Name = "Princess";
princess.Sex = 'F';
princess.Weight = 7.4f;
session.Save(princess);
tx.Commit();
NHibernateHelper.CloseSession();
在一个ISession,每一个数据库操作发生在一个事务内,这样可以隔离每个数据库操作(甚至是Read-only操作)。我们使用Nhibernate的Itransaction API去抽象根本的事务策略(在我们这里,就是ADO.NET事务)。请注意:这个例子没有处理任何异常。
同时请注意:你通过调用NhibernateHelper.GetCurrentSession();正如你所想的,你将得到HTTP请求的当前连接。当你的单元工作完成了以后,你必须确保在应用程序类的Application_EndRequest事件处理程序或在HTTP相应被发送以前的HttpModule中去关闭ISession。接下来的非常好的边缘效应是简单的延迟初始化:当界面呈现后,ISession是仍然打开的,因此当你定位到目标时,Nhibernate可以加载未初始化对象。
Nhibernate有多种方法用于从数据库检索数据。最灵活的方法是通过Hibernate Query Language(HQL),这个语言是容易学习和对SQL的强大的面向对象扩展。
ITransaction tx = session.BeginTransaction();
IQuery query = session.CreateQuery("select c from Cat as c where c.Sex = :sex");
query.SetCharacter("sex", 'F');
foreach (Cat cat in query.Enumerable())
{
Console.Out.WriteLine("Female Cat: " + cat.Name);
}
tx.Commit();
Nhibernate通过标准API提供了面向对象查询,这个标准API用来格式化类型安全的查询。Nhibernate通过使用IdbCommands和参数绑定到所有的SQL信息中。你也能使用Nhibernate的直接SQL查询或者从ISession中得到一个原始的ADO.NET连接。
1.5总结
在这个小教程中,我们只能浏览到Nhibernate的很小一部分的功能。请注意:我们并没有包含任何ASP.NET代码。你必须创建一个ASP.NET页,然后插入Nhibernate。
请记住,Nhibernate做为一个数据访问层,是紧紧集成到你的应用程序中。通常,所有其它的层,依赖持久化机制。你必须确保了解设计的含意。