SummerRain

软件开发/信息安全
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

NHibernate初学体验进阶篇

Posted on 2008-03-18 17:56  SummerRain  阅读(2244)  评论(23编辑  收藏  举报
 

xchuntao@163.com QQ:23106676

在上篇NHibernate初学体检记》中,我参照NHibernate官方快速指南写了两个示例项目,在示例2的源码中充斥了如下类似的代码:

            Configuration cfg = new Configuration();

            cfg.AddAssembly("NHibernate.Examples");

            ISessionFactory factory = cfg.BuildSessionFactory();

            ISession session = factory.OpenSession();

            ITransaction transaction = session.BeginTransaction();

 

如何解决这个问题呢?答案就是采用DAOData 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

我们来创建一个抽象的DAOAbstractDAO,作为超类,让其它的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,这样只有子类可调用它们。看看我们现在继承自AbstractDAOUserDAO是不是简化了:

    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)!

(补注:虽然编写实体DAO类代码简化了许多,但是这些实体DAO还是非常的相似,重复性还是很大的,通过设计泛型的AbstractDAO可以不用写具体的实体DAO了,太棒了!请参考文后评论中的6楼10楼的内容.)

    不过,不要高兴的太早,还没完呢!

 

三、提高效率(引入单例模式)

看看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;

        }

    }

 

(注:本实现参考了MSDNhttp://msdn2.microsoft.com/zh-cn/library/ms998558.aspx 《在 C# 中实现 Singleton》中的内容,有关Singleton或更多设计模式推荐阅读《大话设计模式》一书,很适合初学者的一本好书!)

有了单例的NHibernateFactory,我们的StartOperation()将变为:

private void StartOperation()

        {

            session = NHibernateFactory.OpenSession();

            tx = session.BeginTransaction();

        }

 

啊哈,恭喜你成功进阶!

 

听说SpringHibernate有更好的封装,那么Spring.NET中应该也有对NHibernate的封装吧,有空再说J

 

注:本文内容参考了《Hibernate Quickly中文版》P144-154.

文中内容不妥之处,敬请各位高手指教!

 

本文示例源码下载:/Files/bluesky521/NHibernateQuickStart3.rar

测试环境:单机安装Win2003SP2 + SQL2000 + .NET2.0 + VS2005