在上篇《NHibernate初学体检记》中,我参照NHibernate官方快速指南写了两个示例项目,在示例2的源码中充斥了如下类似的代码:<?XML:NAMESPACE PREFIX = O />
Configuration cfg = new Configuration(); cfg.AddAssembly("NHibernate.Examples"); ISessionFactory factory = cfg.BuildSessionFactory(); ISession session = factory.OpenSession(); ITransaction transaction = session.BeginTransaction(); |
如何解决这个问题呢?答案就是采用DAO(Data Access Object)模式。
一、编写DAO
DAO其实就是把对实体的基本CRUD(创建、读取、更新、删除)操作进行封装。在本示例中也就是把对User实体的持久化操作进行封装,看下代码就什么都清楚了(相比以前的简单代码,我又加入了异常处理部分J):
public class UserDAO { private ISession session; private ITransaction tx;
public void Create(User newUser) { try { StartOperation(); session.Save(newUser); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
public void Update(User newUser) { try { StartOperation(); session.Update(newUser); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
public void Delete(User user) { try { StartOperation(); session.Delete(user); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
public User Find(string id) { User user = null; try { StartOperation(); user = session.Get<User>(id); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); }
return user; }
public IList FindAll() { IList userList = null; try { StartOperation(); userList = session.CreateCriteria(typeof(User)).List(); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } return userList; }
//--------------------------------------------------- private void StartOperation() { Configuration cfg = new Configuration(); cfg.AddAssembly("NHibernate. Examples");
ISessionFactory factory = cfg.BuildSessionFactory();
session = factory.OpenSession(); tx = session.BeginTransaction(); }
private void HandleException(HibernateException e) { tx.Rollback(); throw e; //注:你可以在此写自己的异常处理,如记录日志... } } |
有了UserDAO我们对User实体的操作简化为简单的两行代码(如下添加用户的示例):
User newUser = new User(); newUser.Id = txtLogonID.Text.Trim(); newUser.UserName = txtName.Text.Trim(); newUser.Password = txtPassword.Text.Trim(); newUser.EmailAddress = txtEmailAddress.Text.Trim(); newUser.LastLogon = DateTime.Now; //---------------------------------------------------- UserDAO userDAO = new UserDAO(); userDAO.Create(newUser); |
NHibernate的那些充斥期间的初始化和收尾代码不见了,DAO模式明显降低了应用程序与NHibernate的耦合度。看起来不错J,不过,这里有潜在的重复问题:我们的示例比较简单,只有一个User实体类,正常的项目中会有大量这样的的实体类,也就会有大量对应的DAO类,我们的“复制/粘贴”恶梦开始了,你要为所有的DAO类编写类似于UserDAO类的代码,这里面明显有很多的重复,我们再写其它的DAO类时,需要改变的仅仅是实体类,其余代码都是“复制/粘贴”来的。“复制/粘贴”----所有编程问题的根源!(摘自《Hibernate Quickly中文版》P149)。这时候我们就需要“抽象”了!(突然觉得“抽象”是不是“抽出那些相象的部分”之意,哈哈!)
二、抽象DAO
我们来创建一个抽象的DAO类AbstractDAO,作为超类,让其它的DAO继承之。AbstractDAO封装那些“相象”的部分,以简化实体DAO的编写。看代码吧:
public abstract class AbstractDAO { private ISession session; private ITransaction tx;
protected void Save(Object obj) { try { StartOperation(); session.Save(obj); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
protected void Update(Object obj) { try { StartOperation(); session.Update(obj); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
protected void Delete(Object obj) { try { StartOperation(); session.Delete(obj); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); } }
protected Object Find(System.Type clazz, Object id) { Object obj = null; try { StartOperation(); obj = session.Get(clazz,id); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); }
return obj; }
protected IList FindAll(System.Type clazz) { IList objList = null; try { StartOperation(); objList = session.CreateCriteria(clazz).List(); tx.Commit(); } catch (HibernateException e) { HandleException(e); } finally { session.Close(); }
return objList; }
//--------------------------------------------------- private void StartOperation() { Configuration cfg = new Configuration(); cfg.AddAssembly("NHibernate. Examples"); ISessionFactory factory = cfg.BuildSessionFactory(); session = factory.OpenSession(); tx = session.BeginTransaction(); }
private void HandleException(HibernateException e) { tx.Rollback(); throw e; //注:你可以在此写自己的异常处理,如记录日志... } } |
我们将通用的CRUD方法(包括save/update/delete/find)都放到了AbstractDAO类中,并将这些方法设为protected,这样只有子类可调用它们。看看我们现在继承自AbstractDAO的UserDAO是不是简化了:
public class UserDAO : AbstractDAO { public void Create(User newUser) { base.Save(newUser); }
public void Update(User newUser) { base.Update(newUser); }
public void Delete(User user) { base.Delete(user); }
public User Find(string id) { return (User)base.Find(typeof(User), id); }
public IList FindAll() { return (IList)base.FindAll(typeof(User)); }
} |
哈哈,UserDAO中该有的有,不该有的没有了,世界看起来清爽多了!呼吸下新鲜的空气吧,不用为写更多的实体DAO类发愁了(如果还有代码自动生成工具那就更好了----懒惰的程序员,呵呵J)!
不过,不要高兴的太早,还没完呢!
三、提高效率(引入单例模式)
看看AbstractDAO中每个CRUD方法都要调用的函数StartOperation(),它包含创建ISessionFactory对象的核心代码,这个创建过程需要加载NHibernate映射文件信息,内存开销非常大,每个CRUD方法都要进行重复的创建,这还得了!还好我们有Singleton(单例模式)对付他!Singleton保证了一个类只被实例化一次,它将避免我们的重复加载映射文件信息的问题。以下是我们的实现:
public sealed class NHibernateFactory { private static volatile ISessionFactory factory; private static object syncRoot = new Object(); private NHibernateFactory() { }
public static ISessionFactory BuildIfNeeded() { if (factory == null) { lock (syncRoot) { if (factory == null) { Configuration cfg = new Configuration(); cfg.AddAssembly("NHibernate. Examples"); factory = cfg.BuildSessionFactory(); } } } return factory; }
//---------------------------------- static public ISession OpenSession() { NHibernateFactory.BuildIfNeeded(); ISession session = factory.OpenSession(); return session; } } |
(注:本实现参考了MSDN:http://msdn2.microsoft.com/zh-cn/library/ms998558.aspx 《在 C# 中实现 Singleton》中的内容,有关Singleton或更多设计模式推荐阅读《大话设计模式》一书,很适合初学者的一本好书!)
有了单例的NHibernateFactory,我们的StartOperation()将变为:
private void StartOperation() { session = NHibernateFactory.OpenSession(); tx = session.BeginTransaction(); } |
啊哈,恭喜你成功进阶!
听说Spring对Hibernate有更好的封装,那么Spring.NET中应该也有对NHibernate的封装吧,有空再说J!
注:本文内容参考了《Hibernate Quickly中文版》P144-154.
文中内容不妥之处,敬请各位高手指教!
本文示例源码下载:/Files/bluesky521/NHibernateQuickStart3.rar。
测试环境:单机安装Win2003SP2 + SQL2000 + .NET2.0 + VS2005