您好,登錄后才能下訂單哦!
這篇文章主要介紹了PetShop數(shù)據(jù)訪問層數(shù)中據(jù)庫訪問設(shè)計(jì)的示例分析,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
PetShop數(shù)據(jù)訪問層之?dāng)?shù)據(jù)庫訪問設(shè)計(jì)
在系列一中,我從整體上分析了PetShop的架構(gòu)設(shè)計(jì),并提及了分層的概念。從本部分開始,我將依次對(duì)各層進(jìn)行代碼級(jí)的分析,以求獲得更加細(xì)致而深入的理解。在PetShop 4.0中,由于引入了ASP.Net 2.0的一些新特色,所以數(shù)據(jù)層的內(nèi)容也更加的廣泛和復(fù)雜,包括:數(shù)據(jù)庫訪問、Messaging、MemberShip、Profile四部分。在系列二中,我將介紹有關(guān)數(shù)據(jù)庫訪問的設(shè)計(jì)。
在PetShop中,系統(tǒng)需要處理的數(shù)據(jù)庫對(duì)象分為兩類:一是數(shù)據(jù)實(shí)體,對(duì)應(yīng)數(shù)據(jù)庫中相應(yīng)的數(shù)據(jù)表。它們沒有行為,僅用于表現(xiàn)對(duì)象的數(shù)據(jù)。這些實(shí)體類都被放到Model程序集中,例如數(shù)據(jù)表Order對(duì)應(yīng)的實(shí)體類OrderInfo,其類圖如下:
這些對(duì)象并不具有持久化的功能,簡(jiǎn)單地說,它們是作為數(shù)據(jù)的載體,便于業(yè)務(wù)邏輯針對(duì)相應(yīng)數(shù)據(jù)表進(jìn)行讀/寫操作。雖然這些類的屬性分別映射了數(shù)據(jù)表的列,而每一個(gè)對(duì)象實(shí)例也恰恰對(duì)應(yīng)于數(shù)據(jù)表的每一行,但這些實(shí)體類卻并不具備對(duì)應(yīng)的數(shù)據(jù)庫訪問能力。
由于數(shù)據(jù)訪問層和業(yè)務(wù)邏輯層都將對(duì)這些數(shù)據(jù)實(shí)體進(jìn)行操作,因此程序集Model會(huì)被這兩層的模塊所引用。
第二類數(shù)據(jù)庫對(duì)象則是數(shù)據(jù)的業(yè)務(wù)邏輯對(duì)象。這里所指的業(yè)務(wù)邏輯,并非業(yè)務(wù)邏輯層意義上的領(lǐng)域(domain)業(yè)務(wù)邏輯(從這個(gè)意義上,我更傾向于將業(yè)務(wù)邏輯層稱為“領(lǐng)域邏輯層”),一般意義上說,這些業(yè)務(wù)邏輯即為基本的數(shù)據(jù)庫操作,包括Select,Insert,Update和Delete。由于這些業(yè)務(wù)邏輯對(duì)象,僅具有行為而與數(shù)據(jù)無關(guān),因此它們均被抽象為一個(gè)單獨(dú)的接口模塊IDAL,例如數(shù)據(jù)表Order對(duì)應(yīng)的接口IOrder:
將數(shù)據(jù)實(shí)體與相關(guān)的數(shù)據(jù)庫操作分離出來,符合面向?qū)ο蟮木?。首先,它體現(xiàn)了“職責(zé)分離”的原則。將數(shù)據(jù)實(shí)體與其行為分開,使得兩者之間依賴減弱,當(dāng)數(shù)據(jù)行為發(fā)生改變時(shí),并不影響Model模塊中的數(shù)據(jù)實(shí)體對(duì)象,避免了因一個(gè)類職責(zé)過多、過大,從而導(dǎo)致該類的引用者發(fā)生“災(zāi)難性”的影響。其次,它體現(xiàn)了“抽象”的精神,或者說是“面向接口編程”的最佳體現(xiàn)。抽象的接口模塊IDAL,與具體的數(shù)據(jù)庫訪問實(shí)現(xiàn)完全隔離。這種與實(shí)現(xiàn)無關(guān)的設(shè)計(jì),保證了系統(tǒng)的可擴(kuò)展性,同時(shí)也保證了數(shù)據(jù)庫的可移植性。在PetShop中,可以支持SQL Server和Oracle,那么它們具體的實(shí)現(xiàn)就分別放在兩個(gè)不同的模塊SQLServerDAL、OracleDAL中。
以O(shè)rder為例,在SQLServerDAL、OracleDAL兩個(gè)模塊中,有不同的實(shí)現(xiàn),但它們同時(shí)又都實(shí)現(xiàn)了IOrder接口,如圖:
從數(shù)據(jù)庫的實(shí)現(xiàn)來看,PetShop體現(xiàn)出了沒有ORM框架的臃腫與丑陋。由于要對(duì)數(shù)據(jù)表進(jìn)行Insert和Select操作,以SQL Server為例,就使用了SqlCommand,SqlParameter,SqlDataReader等對(duì)象,以完成這些操作。尤其復(fù)雜的是Parameter的傳遞,在PetShop中,使用了大量的字符串常量來保存參數(shù)的名稱。此外,PetShop還專門為SQL Server和Oracle提供了抽象的Helper類,包裝了一些常用的操作,如ExecuteNonQuery、ExecuteReader等方法。
在沒有ORM的情況下,使用Helper類是一個(gè)比較好的策略,利用它來完成數(shù)據(jù)庫基本操作的封裝,可以減少很多和數(shù)據(jù)庫操作有關(guān)的代碼,這體現(xiàn)了對(duì)象復(fù)用的原則。PetShop將這些Helper類統(tǒng)一放到DBUtility模塊中,不同數(shù)據(jù)庫的Helper類暴露的方法基本相同,只除了一些特殊的要求,例如Oracle中處理bool類型的方式就和SQL Server不同,從而專門提供了OraBit和OraBool方法。此外,Helper類中的方法均為static方法,以利于調(diào)用。OracleHelper的類圖如下:
對(duì)于數(shù)據(jù)訪問層來說,最頭疼的是SQL語句的處理。在早期的CS結(jié)構(gòu)中,由于未采用三層式架構(gòu)設(shè)計(jì),數(shù)據(jù)訪問層和業(yè)務(wù)邏輯層是緊密糅合在一起的,因此,SQL語句遍布與系統(tǒng)的每一個(gè)角落。這給程序的維護(hù)帶來極大的困難。此外,由于Oracle使用的是PL-SQL,而SQL Server和Sybase等使用的是T-SQL,兩者雖然都遵循了標(biāo)準(zhǔn)SQL的語法,但在很多細(xì)節(jié)上仍有區(qū)別,如果將SQL語句大量的使用到程序中,無疑為可能的數(shù)據(jù)庫移植也帶來了困難。
最好的方法是采用存儲(chǔ)過程。這種方法使得程序更加整潔,此外,由于存儲(chǔ)過程可以以數(shù)據(jù)庫腳本的形式存在,也便于移植和修改。但這種方式仍然有缺陷。一是存儲(chǔ)過程的測(cè)試相對(duì)困難。雖然有相應(yīng)的調(diào)試工具,但比起對(duì)代碼的調(diào)試而言,仍然比較復(fù)雜且不方便。二是對(duì)系統(tǒng)的更新帶來障礙。如果數(shù)據(jù)庫訪問是由程序完成,在.Net平臺(tái)下,我們僅需要在修改程序后,將重新編譯的程序集xcopy到部署的服務(wù)器上即可。如果使用了存儲(chǔ)過程,出于安全的考慮,必須有專門的DBA重新運(yùn)行存儲(chǔ)過程的腳本,部署的方式受到了限制。
我曾經(jīng)在一個(gè)項(xiàng)目中,利用一個(gè)專門的表來存放SQL語句。如要使用相關(guān)的SQL語句,就利用關(guān)鍵字搜索獲得對(duì)應(yīng)語句。這種做法近似于存儲(chǔ)過程的調(diào)用,但卻避免了部署上的問題。然而這種方式卻在性能上無法得到保證。它僅適合于SQL語句較少的場(chǎng)景。不過,利用良好的設(shè)計(jì),我們可以為各種業(yè)務(wù)提供不同的表來存放SQL語句。同樣的道理,這些SQL語句也可以存放到XML文件中,更有利于系統(tǒng)的擴(kuò)展或修改。不過前提是,我們需要為它提供專門的SQL語句管理工具。
SQL語句的使用無法避免,如何更好的應(yīng)用SQL語句也無定論,但有一個(gè)原則值得我們遵守,就是“應(yīng)該盡量讓SQL語句盡存在于數(shù)據(jù)訪問層的具體實(shí)現(xiàn)中”。
當(dāng)然,如果應(yīng)用ORM,那么一切就變得不同了。因?yàn)镺RM框架已經(jīng)為數(shù)據(jù)訪問提供了基本的Select,Insert,Update和Delete操作了。例如在NHibernate中,我們可以直接調(diào)用ISession對(duì)象的Save方法,來Insert(或者說是Create)一個(gè)數(shù)據(jù)實(shí)體對(duì)象:
public void Insert(OrderInfo order) { ISession s = Sessions.GetSession(); ITransaction trans = null; try { trans = s.BeginTransaction(); s.Save( order); trans.Commit(); } finally { s.Close(); } }
沒有SQL語句,也沒有那些煩人的Parameters,甚至不需要專門去考慮事務(wù)。此外,這樣的設(shè)計(jì),也是與數(shù)據(jù)庫無關(guān)的,NHibernate可以通過Dialect(方言)的機(jī)制支持不同的數(shù)據(jù)庫。唯一要做的是,我們需要為OrderInfo定義hbm文件。
當(dāng)然,ORM框架并非是萬能的,面對(duì)紛繁復(fù)雜的業(yè)務(wù)邏輯,它并不能完全消滅SQL語句,以及替代復(fù)雜的數(shù)據(jù)庫訪問邏輯,但它卻很好的體現(xiàn)了“80/20(或90/10)法則”(也被稱為“帕累托法則”),也就是說:花比較少(10%-20%)的力氣就可以解決大部分(80%-90%)的問題,而要解決剩下的少部分問題則需要多得多的努力。至少,那些在數(shù)據(jù)訪問層中占據(jù)了絕大部分的CRUD操作,通過利用ORM框架,我們就僅需要付出極少數(shù)時(shí)間和精力來解決它們了。這無疑縮短了整個(gè)項(xiàng)目開發(fā)的周期。
還是回到對(duì)PetShop的討論上來?,F(xiàn)在我們已經(jīng)有了數(shù)據(jù)實(shí)體,數(shù)據(jù)對(duì)象的抽象接口和實(shí)現(xiàn),可以說有關(guān)數(shù)據(jù)庫訪問的主體就已經(jīng)完成了。留待我們的還有兩個(gè)問題需要解決:
1、數(shù)據(jù)對(duì)象創(chuàng)建的管理
2、利于數(shù)據(jù)庫的移植
在PetShop中,要?jiǎng)?chuàng)建的數(shù)據(jù)對(duì)象包括Order,Product,Category,Inventory,Item。在前面的設(shè)計(jì)中,這些對(duì)象已經(jīng)被抽象為對(duì)應(yīng)的接口,而其實(shí)現(xiàn)則根據(jù)數(shù)據(jù)庫的不同而有所不同。也就是說,創(chuàng)建的對(duì)象有多種類別,而每種類別又有不同的實(shí)現(xiàn),這是典型的抽象工廠模式的應(yīng)用場(chǎng)景。而上面所述的兩個(gè)問題,也都可以通過抽象工廠模式來解決。標(biāo)準(zhǔn)的抽象工廠模式類圖如下:
例如,創(chuàng)建SQL Server的Order對(duì)象如下:
PetShopFactory factory = new SQLServerFactory(); IOrder = factory.CreateOrder();
要考慮到數(shù)據(jù)庫的可移植性,則factory必須作為一個(gè)全局變量,并在主程序運(yùn)行時(shí)被實(shí)例化。但這樣的設(shè)計(jì)雖然已經(jīng)達(dá)到了“封裝變化”的目的,但在創(chuàng)建PetShopFactory對(duì)象時(shí),仍不可避免的出現(xiàn)了具體的類SQLServerFactory,也即是說,程序在這個(gè)層面上產(chǎn)生了與SQLServerFactory的強(qiáng)依賴。一旦整個(gè)系統(tǒng)要求支持Oracle,那么還需要修改這行代碼為:
PetShopFactory factory = new OracleFactory();
修改代碼的這種行為顯然是不可接受的。解決的辦法是“依賴注入”?!耙蕾囎⑷搿钡墓δ芡ǔJ怯脤iT的IoC容器提供的,在Java平臺(tái)下,這樣的容器包括Spring,PicoContainer等。而在.Net平臺(tái)下,最常見的則是Spring.Net。不過,在PetShop系統(tǒng)中,并不需要專門的容器來實(shí)現(xiàn)“依賴注入”,簡(jiǎn)單的做法還是利用配置文件和反射功能來實(shí)現(xiàn)。也就是說,我們可以在web.config文件中,配置好具體的Factory對(duì)象的完整的類名。然而,當(dāng)我們利用配置文件和反射功能時(shí),具體工廠的創(chuàng)建就顯得有些“畫蛇添足”了,我們完全可以在配置文件中,直接指向具體的數(shù)據(jù)庫對(duì)象實(shí)現(xiàn)類,例如PetShop.SQLServerDAL.IOrder。那么,抽象工廠模式中的相關(guān)工廠就可以簡(jiǎn)化為一個(gè)工廠類了,所以我將這種模式稱之為“具有簡(jiǎn)單工廠特質(zhì)的抽象工廠模式”,其類圖如下:
DataAccess類完全取代了前面創(chuàng)建的工廠類體系,它是一個(gè)sealed類,其中創(chuàng)建各種數(shù)據(jù)對(duì)象的方法,均為靜態(tài)方法。之所以能用這個(gè)類達(dá)到抽象工廠的目的,是因?yàn)榕渲梦募头瓷涞倪\(yùn)用,如下的代碼片斷所示:
public sealed class DataAccess { // Look up the DAL implementation we should be using private static readonly string path = ConfigurationManager.AppSettings["WebDAL"]; private static readonly string orderPath = ConfigurationManager.AppSettings["OrdersDAL"]; public static PetShop.IDAL.IOrder CreateOrder() { string className = orderPath + ".Order"; return (PetShop.IDAL.IOrder)Assembly.Load(orderPath).CreateInstance(className); } }
在PetShop中,這種依賴配置文件和反射創(chuàng)建對(duì)象的方式極其常見,包括IBLLStategy、CacheDependencyFactory等等。這些實(shí)現(xiàn)邏輯散布于整個(gè)PetShop系統(tǒng)中,在我看來,是可以在此基礎(chǔ)上進(jìn)行重構(gòu)的。也就是說,我們可以為整個(gè)系統(tǒng)提供類似于“Service Locator”的實(shí)現(xiàn):
public static class ServiceLocator { private static readonly string dalPath = ConfigurationManager.AppSettings["WebDAL"]; private static readonly string orderPath = ConfigurationManager.AppSettings["OrdersDAL"]; //…… private static readonly string orderStategyPath = ConfigurationManager.AppSettings["OrderStrategyAssembly"]; public static object LocateDALObject(string className) { string fullPath = dalPath + "." + className; return Assembly.Load(dalPath).CreateInstance(fullPath); } public static object LocateDALOrderObject(string className) { string fullPath = orderPath + "." + className; return Assembly.Load(orderPath).CreateInstance(fullPath); } public static object LocateOrderStrategyObject(string className) { string fullPath = orderStategyPath + "." + className; return Assembly.Load(orderStategyPath).CreateInstance(fullPath); } //…… }
那么和所謂“依賴注入”相關(guān)的代碼都可以利用ServiceLocator來完成。例如類DataAccess就可以簡(jiǎn)化為:
public sealed class DataAccess { public static PetShop.IDAL.IOrder CreateOrder() { return (PetShop.IDAL.IOrder)ServiceLocator. LocateDALOrderObject("Order"); } }
通過ServiceLocator,將所有與配置文件相關(guān)的namespace值統(tǒng)一管理起來,這有利于各種動(dòng)態(tài)創(chuàng)建對(duì)象的管理和未來的維護(hù)。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“PetShop數(shù)據(jù)訪問層數(shù)中據(jù)庫訪問設(shè)計(jì)的示例分析”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。