在上篇《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