什麼是持久層?先解釋什麼是持久,英文persistence,將記憶體中的資料固化,保持在物理儲存裝置中。然而在公司專屬應用程式中,往往通過關係型資料庫來完成這一過程。那麼持久層的定義是:相對於三層架構中的展示層、業務層而言,專門負責持久化資料的獨立領域。 設計模式中的“單一職責”原則確定了分層的目的,說白了,持久層就是專門與資料庫打交道的。1所示
圖1
在PetShop4.0中的DAL(資料庫訪問層)就是操作資料庫的。在其DAL中,通過SQL語句返回DataReader,然後給Model對象賦值;在添加、修改、刪除操作中,通過Model對象的資料產生SQL語句,然後寫入資料庫。此時,我們能夠看出每張表都用同樣的操作。雖然PetShop4.0使用SqlHelper封裝資料庫操作,但是卻沒有一個通用的CRUD封裝。目前PetShop4.0每個表都對應各種的CRUD,這樣就會出現大量重複的代碼。
對於PetShop4.0的使用多鐘資料庫來言。對於每種資料庫都要寫一種DAL。這樣資料庫的可移植性就不強了。
針對上述問題,我使用ORM架構NHibernate作為持久層架構,來取代PetShop4.0的DAL。
什麼是ORM?對象關係映射,英文Object Relational Mapping。是解決“阻抗不匹配”的一種技術。“阻抗不匹配”是指,關係型資料庫中資料與程式設計語言中的對象存在差異。使用物件導向的語言編程的同時又要為“面向關係”而犯愁。在企業級的系統開發中,程式員有30%的時間用來解決“阻抗不匹配”的問題,花大量時間去編寫SQL語句。而ORM架構的出現就能很好的解決這樣的問題,從而做到快速開發。在一個項目的編碼過程中,業務層是比較重要的,則編寫展示層和持久層代碼所用的時間不應該過長。所以使用好一個ORM架構尤為重要。至於為什麼要使用ORM架構,有一個重要的原因:提高開發速度,減少開發和維護成本。然而這一點對於一個商業公司來說相當看重。
ORM的O是對象的意思,代表進階語言中的實體(Entity)模型;R是關係的意思,代表關係型資料庫;M是映射的意思,表示通過某種方式實現對象和關係型資料庫的映射。2所示
圖2
本系列文章,使用NHibernate作為ORM架構。這樣操作對象就相當於操作資料庫,開發人員不需要關心SQL語句怎麼寫,一門心思的專註物件導向的開發,從而使得應用程式更OO(物件導向)。作為NHibernate的實體物件,完全是POJO(上篇文章提到過)。在持久層中,NHibernate架構藉助對應檔和實體所承載的資料自動產生SQL語句,實現自動持久化。但是NHibernate仍然存在一些缺點:如,大量刪除和修改。至於複雜的查詢報表來說,java開發人員往往放棄Hibernate,而使用ibatis或jdbc的方式實現。但是.NET在這一點做的很好,通過NHibernate整合Linq的方式,使我們能夠通過Linq的文法查詢出想要得到的資料。巧妙的把C#文法(更且卻的說是.NET)和java中最流行的架構之一的.NET版NHibernate完美結合起來。可以這麼說,HQL(Hibernate Query Language)不是完全的物件導向,但是Linq to NHibernate是覺對的物件導向。
在調用NHibernate的API操作資料庫時,每個對象都需要寫一個DAO。這裡,我們使用基於泛型的Repository(資產庫)模式將相同的代碼內聚起來。
IRepository
public interface IRepository<T> where T : class
{
/// <summary>
/// 擷取實體
/// </summary>
/// <param name="id">主鍵</param>
/// <returns></returns>
T Get(object id);
/// <summary>
/// 插入實體
/// </summary>
/// <param name="entity">實體</param>
/// <returns></returns>
object Save(T entity);
/// <summary>
/// 修改實體
/// </summary>
/// <param name="entity">實體</param>
/// <returns></returns>
void Update(T entity);
/// <summary>
/// 儲存實體
/// </summary>
/// <param name="entity">實體</param>
/// <returns></returns>
void SaveOrUpdate(T entity);
/// <summary>
/// 刪除實體
/// </summary>
/// <param name="entity">實體</param>
/// <returns></returns>
void Delete(T entity);
/// <summary>
/// 擷取全部集合
/// </summary>
/// <returns></returns>
IQueryable<T> LoadAll();
}
RepositoryBase
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Spring.Data.NHibernate.Generic.Support;
using NHibernate.Linq;
public abstract class RepositoryBase<T> : HibernateDaoSupport, IRepository<T> where T : class
{
public virtual object Save(T entity)
{
return this.HibernateTemplate.Save(entity);
}
public virtual T Get(object id)
{
return this.HibernateTemplate.Get<T>(id);
}
public virtual IQueryable<T> LoadAll()
{
return this.Session.Linq<T>();
}
public virtual void Update(T entity)
{
this.HibernateTemplate.Update(entity);
}
public void Delete(T entity)
{
this.HibernateTemplate.Delete(entity);
}
public virtual void SaveOrUpdate(T entity)
{
this.HibernateTemplate.SaveOrUpdate(entity);
}
}
這樣,同為增、刪、改、查的代碼,我只需要寫一遍就可以了。對於資料庫的遷移,可以通過設定資料庫方言來實現其目的。在使用Spring.NET作為IoC架構中,我們不用關心和有意去編寫資料庫事務,並且NHibernate的Session管理也交給Spring.NET來處理。重構後的代碼與PetShop4.0的DAL代碼比較起來,程式碼數減少了很多,但代碼的可維護性和靈活性卻有明顯的提升。