** NHibernate ** ** 进阶 ** ** **
作者: Justin Gehtland
( 原文:请点击这里 )
在我的最近文章中,我介绍了 Nhibernate。在这里和在其他的论坛中 , 因为我没有强调NHibernate 只是许多可得的 ORM 解决方案之一,(事实上,现在对.NET开发者来说,开源的加上商业的 ORM 的架构现在是超过 50个可供选择)。 作为一个开发顾问,我会经常用到H ibernate( 大家都知道它吧 ) 既然我必须在 .NET 平台下选择一个, NHibernate 是我最明智的选择。 我想在这里脱离.NET平台来研究一下这个框架,并谈谈我们能从它获得的益处。
我的上一篇文章中介始示范了应用Nhibernate建一个简单应用(大学注册登记)的起步示例。下面我将介绍更多使用Nhibernate的技术,这次我们的目标给你一些在对特别大数据量查询的高吞吐量、高并发性的应用需求下使用Nhibernate的方式。
为了达到这个目标,我将分成下面四部分来讲:最佳的 Session 管理方式,指导使用 HQL 查询数据库,怎样延迟载入对象集合及对象生存周期管理。
** 第一部分:会话管理 ** ** **
在我的前一篇文章中 , 我实现了一个非常简单的为管理 SessionFactory 和 Sessionr 的 名为 RegMgr 的类 :它在其构造函数中根据配置创建了一个 SessionFactory, 然后在每一个方法中使用 SessionFactory 创建 Session ,使用这种方式有几个问题。
首先, 每个 RegMgr 对象都需要建造一个新的 SessionFactory, 这将耗费非常昂贵资源。 每个 SessionFactory 都意味对你的应用中的实体与相应的数据库进行一次建模。其实仅仅在你应用需要多个数据库的支持才需创建多个 SessionFactory 的实例,而我的简单例子不是,所以无需这样。
第二,每个方法从这个Factory创建了一个新的Session。这种方式每当方法完成都会关闭Session并释放锁定的数据库,从而提供了最大程度的并发保护机制。然后这也结束这个会话所有关联对象的状态,其实Session创建也比较耗费资源的,虽然不像SessionFactory那样。
第一个问题的答案是可以肯定的,那就是确认你只创建 SessionFactory 一次。 RegMgr 原来是这样的 :
public class RegMgr : DBMgr
{
Configuration config;
ISessionFactory factory;
public RegMgr()
{
try
{
config = new Configuration();
config.AddClass(typeof(nhRegistration.Department));
config.AddClass(typeof(nhRegistration.Person));
config.AddClass(typeof(
nhRegistration.UniversityClass));
factory = config.BuildSessionFactory();
}
catch (Exception ex)
{
// handle exception
}
}
// etc...
}
应修改如下:
public class RegMgr : DBMgr
{
static Configuration config;
static ISessionFactory factory;
static RegMgr()
{
try
{
config = new Configuration();
config.AddClass(typeof(nhRegistration.Department));
config.AddClass(typeof(nhRegistration.Person));
config.AddClass(typeof(
nhRegistration.UniversityClass));
factory = config.BuildSessionFactory();
}
catch (Exception ex)
{
// handle exception
}
}
// etc...
}
这样,所有 RegMgr 的实例所共享了这同一个 SessionFactory ,这就节省大量资源。
看起来用同样的方式来解决Session管理的问题也会一样好,那为什么不将一个Static的Session加入到这个类中并仅仅在那个SessionFactory中创建并打开它呢,然后所有的方法都使用同一个Session。答案有两个:首先,共享的Static的Session被多个线程同时访问时可能是线程不安全的,各个事物(Transiction)会相互干扰的;其次,它也大部分时处在空闲状态,但是还是保持一物理数据库连接。而数据库连接是非常珍贵的资源。每一种数据访问方式都有一个相同的目标就是使服务端的游标与一个客户端的连接维持在最短的时间内,Nhibernate也同样如此。因此,无所事事开着的Session相反会是一个很费资源的方式。
替代的解决方案是使用一个非连接态的Session对象:为你所有调用的函数创建一个共享的Session实例或静态对象,在其每个函数的调用开始打开它,离开函数总是关闭它。代码可能是这样的:
public IList getClasses()
{
IList classes = null;
try
{
// session is declared in instance scope
session.Reconnect();
ITransaction tx = session.BeginTransaction();
classes = session.CreateCriteria(
typeof(UniversityClass)).List();
}
catch (Exception ex)
{
// handle exception
}
finally
{
session.Flush();
session.Disconnect();
}
return classes;
}
这样你总能重用同一个Session对象,这样不同方法因调用Session所花费的资源会减到最少,而数据库的资源也都会因及时释放也会使它们协同工作得很好。
** 第二部分 ** ** :HQL **
我们有时需要 NHibernate 来处理如:“查询所有部门”或“查询某个学生”之类更特殊更复杂的需求。具体应用中可能需要提供复杂的搜索与数据过滤操作来执行数据查询。 NHibernate 提供了与 SQL 很相似的 HQL( the Hibernate Query Language ) 。 在 HQL 和 SQL 之间的最大不同点在FROM子句:在HQL中可以使用一个或多个类来表示SQL中的一个或多个表名。既然 NHibernate 的目标就是用Domain objects来提供一个透时的数据层,既然Domain objects与数据表具有对应关系,那么就不再需要提供表名给Nhibernate了。
举例来说 , 在我的 “ University registration ” 程序中 , 我们可能想知道那些班已安排好但还没有分配任课教师。知道哪一班级已经被预定但是尚未被分配Professors会更好。( 顺便我们可能需要知道其中那些是一年级的班级)。我们为得到它们可以在RegMgr中添加这样一个方法:
public IList getClassesNoProf(){}
然后使用 NHibernate 来进行完整的实现:
IList classes = this.getClasses();
IList results = new ArrayList();
foreach(UniversityClass c in classes)
{
if(c.Prof == null) results.Add(c);
}
return results;
这里我从数据库获取所有的班级然后遍历它们,并获得最终符合条件的班级。 这样可能会很慢,因为获取了所有对象而最后能需要其中一些甚至很少的一部分,数据表越大这种方式会越慢而难以接受。
所以我们要做的是数据库中直接过滤,这可使数据在我们程序之间的网络带宽需求降至最低(因为数据库通常在网络中的其它服务器上)。
public IList getClassesNoProf()
{
IList results = null;
try
{
ISession session = factory.OpenSession();
results = session.Find(
"from nhRegistration.UniversityClass as
uc where personid is null");
}
catch (Exception ex)
{
// handle exception
}
return results;
实现一个 HQL 查询主要是通过调用 ISession.Find() 方法。 使用它可以执行任意一个查询。 HQL 支持几乎所有 SQL 子句 , 如排序和分组 ( order by,group by ) 。为了得到相同的而按Name属性排序的 UniversityClass 对象集合只需对这个查询做这样的改变就以了 :
results = session.Find("from nhRegistration.UniversityClass as
uc where personid is null order by uc.Name asc");
** 特别注意: ** 审查排序子句的语法。这里我们按Name来排序,如果你参考我上一篇文章中的映射文件中就有一个名为 “classname”的列。当书写Order子句时,既可以使用属性名也可以数据表的列名,但是如果选择属性你就不得不使用类的别名作为前缀(这个例中就是uc),而使用列名时则不需要( “order by classname asc” )。我宁愿使用类别名加上属性我的方式,因为使我的代码与数据库相分离。
这个 HQL 查询缺少普通 SQL 中的 SELECT 子句 , 这实际暗示地告诉 NHibernate 返回所有 From 中