簡單解析J2EE數(shù)據(jù)持久層設(shè)計(jì)
Hibernate有很多值得學(xué)習(xí)的地方,這里我們主要介紹在Hibernate中J2EE數(shù)據(jù)持久層設(shè)計(jì)。
數(shù)據(jù)持久層的設(shè)計(jì)目標(biāo)是為整個(gè)項(xiàng)目提供一個(gè)高層、統(tǒng)一、安全和并發(fā)的數(shù)據(jù)持久機(jī)制。
完成對(duì)各種數(shù)據(jù)進(jìn)行持久化的編程工作,并為系統(tǒng)業(yè)務(wù)邏輯層提供服務(wù)。數(shù)據(jù)持久層提供了數(shù)據(jù)訪問方法,能夠使其它程序員避免手工編寫程序訪問數(shù)據(jù)持久層(Persistene layer),使其專注于業(yè)務(wù)邏輯的開發(fā),并且能夠在不同項(xiàng)目中重用映射框架,大大簡化了數(shù)據(jù)增、刪、改、查等功能的開發(fā)過程,同時(shí)又不喪失多層結(jié)構(gòu)的天然優(yōu)勢(shì),繼承延續(xù)J2EE特有的可伸縮性和可擴(kuò)展性。
1 數(shù)據(jù)持久層及ORM映射框架
筆者從事的項(xiàng)目中的數(shù)據(jù)持久層,是基于J2EE體系結(jié)構(gòu),并采用了Hibernate作為持久映射框架。
Hibernate是一種新的ORM映射工具,是JDBC的輕量級(jí)的對(duì)象封裝。Hibernate可以用在JDBC可以使用的任何場合,例如Java應(yīng)用程序的數(shù)據(jù)庫訪問代碼,DAO接口的實(shí)現(xiàn)類,甚至可以是BMP里面的訪問數(shù)據(jù)庫的代碼。Hibernate不僅提供了從Java類到數(shù)據(jù)表之間的映射,也提供了數(shù)據(jù)查詢和恢復(fù)機(jī)制。相對(duì)于使用JDBC和SQL來手工操作數(shù)據(jù)庫,使用Hibernate,可以大大減少操作數(shù)據(jù)庫的工作量。
Hibernate是一個(gè)和JDBC密切關(guān)聯(lián)的、獨(dú)立的對(duì)象持久層框架,可以搭配各種App Server、Web Server、EJB Container共同使用,Hibernate的兼容性僅同JDBC驅(qū)動(dòng)、底層數(shù)據(jù)庫產(chǎn)品間有一定的關(guān)系,但是和使用它的Java程序、App Server沒有任何關(guān)系,也不存在兼容性問題。而且事實(shí)表明Hibernate可以和多種Web服務(wù)器或者應(yīng)用服務(wù)器良好集成,如今已經(jīng)支持幾乎所有的流行的數(shù)據(jù)庫服務(wù)器(達(dá)16種)。
在較為常用的數(shù)據(jù)持久層方案中,Hibernate無疑是最優(yōu)秀的,下面是對(duì)各種持久方案的比較。
¨ 流行的數(shù)據(jù)持久層架構(gòu):
Business Layer <-> Session Bean <-> Entity Bean <-> DB
¨ 為了解決性能障礙的替代架構(gòu):
Business Layer <-> DAO <-> JDBC <-> DB
¨ 使用Hibernate來提高上面架構(gòu)的開發(fā)效率的架構(gòu):
Business Layer <-> DAO <-> Hibernate <-> DB
我們就上面3個(gè)架構(gòu)來作如下分析。
(1)內(nèi)存消耗:采用JDBC的架構(gòu)無疑是最省內(nèi)存的,Hibernate的架構(gòu)次之,EB的架構(gòu)最差。
(2)運(yùn)行效率:如果JDBC的代碼寫的非常優(yōu)化,那么JDBC架構(gòu)運(yùn)行效率最高,但是實(shí)際項(xiàng)目中,這一點(diǎn)幾乎做不到,這需要程序員非常精通JDBC,運(yùn)用Batch語句,調(diào)整PreapredStatement的Batch Size和Fetch Size等參數(shù),以及在必要的情況下采用結(jié)果集cache等等。而一般情況下程序員是做不到這一點(diǎn)的。因此Hibernate架構(gòu)表現(xiàn)出最快的運(yùn)行效率。EB的架構(gòu)效率會(huì)差的很遠(yuǎn)。
(3)開發(fā)效率:在有Eclipse、JBuilder等開發(fā)工具的支持下,對(duì)于簡單的項(xiàng)目,EB架構(gòu)開發(fā)效率最高,JDBC次之,Hibernate最差。但是在大的項(xiàng)目,特別是持久層關(guān)系映射很復(fù)雜的情況下,Hibernate效率高的驚人,JDBC次之,而EB架構(gòu)很可能會(huì)失敗。
2 數(shù)據(jù)持久層設(shè)計(jì)
復(fù)雜性是應(yīng)用開發(fā)過程中最令人頭疼的一個(gè)問題。每當(dāng)在一個(gè)應(yīng)用中增加一個(gè)功能時(shí),它的復(fù)雜性通常呈幾何級(jí)的增長。這種復(fù)雜性往往導(dǎo)致程序的開發(fā)無法再繼續(xù)下去。這也是現(xiàn)在為什么許多應(yīng)用只有Beta版本而沒有正式版的原因。
專家將應(yīng)用開發(fā)過程產(chǎn)生的復(fù)雜性分為兩類,即非本質(zhì)的(accidental)和本質(zhì)的(essential)。本質(zhì)的復(fù)雜性是對(duì)于解決目標(biāo)問題所必然產(chǎn)生的復(fù)雜性,非本質(zhì)的復(fù)雜性是由于選擇了不適當(dāng)?shù)拈_發(fā)工具和設(shè)計(jì)工具而產(chǎn)生的復(fù)雜性。對(duì)于一個(gè)功能確定的程序來講,本質(zhì)的復(fù)雜性是確定的,而非本質(zhì)的復(fù)雜性則是沒有限制的。因此,一個(gè)應(yīng)用的開發(fā)要想較順利地取得成功,就需要盡可能地減少非本質(zhì)的復(fù)雜性。
設(shè)計(jì)模式使人們可以更加簡單方便地復(fù)用成功的設(shè)計(jì)和體系結(jié)構(gòu)。將已證實(shí)的技術(shù)表述成設(shè)計(jì)模式,也會(huì)使新系統(tǒng)開發(fā)者更加容易理解其設(shè)計(jì)思路。
衡量一個(gè)系統(tǒng)優(yōu)秀與否的關(guān)鍵因素,除了能夠滿足用戶需求外還有如下方面:首先是靈活性。靈活性意指這種結(jié)構(gòu)或模式不依賴于任何實(shí)際應(yīng)用,應(yīng)該與操作系統(tǒng)、應(yīng)用程序無關(guān)。提供獨(dú)立的結(jié)構(gòu),可以提供最大的重用。其次是可擴(kuò)展性。隨著業(yè)務(wù)的擴(kuò)展,新的業(yè)務(wù)不斷增加,業(yè)務(wù)邏輯自然增加,系統(tǒng)必然會(huì)進(jìn)行修改或添加相應(yīng)功能模塊。再次是可配置性。最后是安全性。
數(shù)據(jù)持久層的設(shè)計(jì)采納了多種設(shè)計(jì)模式,最大限度的降低了系統(tǒng)內(nèi)部各模塊、子系統(tǒng)間的耦合性,使得系統(tǒng)相對(duì)易于擴(kuò)展,并且能夠在進(jìn)行改變時(shí),保證持久層的業(yè)務(wù)邏輯層相對(duì)穩(wěn)定,基本不需要因持久層的調(diào)整改變而進(jìn)行邏輯層的變動(dòng)。
筆者在項(xiàng)目中采用了如下設(shè)計(jì)模式。
1) 整體架構(gòu)——MVC模式(模型-視圖-控制器)
模型(Model):模型包含完成任務(wù)所需要的所有的行為和數(shù)據(jù)。在數(shù)據(jù)持久層中,模型即為值對(duì)象以及數(shù)據(jù)訪問對(duì)象。
視圖(View):數(shù)據(jù)持久層中,視圖就是持久層同其它層進(jìn)行數(shù)據(jù)交換的值對(duì)象(Transfer Object)和視圖助手對(duì)象。
¨ 控制器(Controller):持久層所需的控制相對(duì)簡單,因此集成到了控制代理中。
持久層整體采用MVC模式,使得整個(gè)數(shù)據(jù)持久層的實(shí)現(xiàn)部分與項(xiàng)目的業(yè)務(wù)邏輯部分隔離開來,能夠?qū)崿F(xiàn)對(duì)接口作大的修改而不需要對(duì)相應(yīng)的模型進(jìn)行修改。另外,持久層某子系統(tǒng)發(fā)生變化時(shí),不會(huì)影響到其它子系統(tǒng)。有利于提高系統(tǒng)的穩(wěn)定性、可維護(hù)性。
2) 值對(duì)象模式(Value Object Pattern)
值對(duì)象用來封裝業(yè)務(wù)對(duì)象。相應(yīng)的方法調(diào)用是設(shè)置(getter)和檢索(setter)值對(duì)象。它是任意的可串行化的Java對(duì)象,當(dāng)客戶端Bean請(qǐng)求業(yè)務(wù)數(shù)據(jù)時(shí),該Bean可以構(gòu)造值對(duì)象,用屬性值來填充,并按照值把它傳遞給客戶端。
在筆者開發(fā)項(xiàng)目的數(shù)據(jù)持久層體系結(jié)構(gòu)中,值對(duì)象主要應(yīng)用在子系統(tǒng)間傳遞、交換數(shù)據(jù)(Transfer Object)和映射數(shù)據(jù)表兩個(gè)方面(Persistent Object)。
在各子系統(tǒng)間進(jìn)行數(shù)據(jù)傳遞和數(shù)據(jù)交換時(shí),使用值對(duì)象模式能夠最大化地降低系統(tǒng)間數(shù)據(jù)傳遞的開銷。在這種策略下傳遞的是對(duì)象而不再是一個(gè)個(gè)的有意義的數(shù)據(jù),使得系統(tǒng)在進(jìn)行擴(kuò)充、修改時(shí),各子系統(tǒng)間數(shù)據(jù)傳遞部分不會(huì)受到影響,因?yàn)楦髯酉到y(tǒng)僅需要關(guān)心是否有值對(duì)象被傳遞,而并不去關(guān)心傳遞的到底是什么數(shù)據(jù)。
在映射數(shù)據(jù)庫表時(shí),值對(duì)象類及其子類所構(gòu)成的樹形結(jié)構(gòu)被用來映射一個(gè)數(shù)據(jù)庫表,該繼承樹通過XML配置文件對(duì)應(yīng)數(shù)據(jù)庫中的單個(gè)表,這使得最底層的關(guān)系型的數(shù)據(jù)庫表結(jié)構(gòu)能夠面向?qū)ο竽P退[藏,另外,由于面向?qū)ο笤O(shè)計(jì)方法中類的可繼承性,采用繼承樹對(duì)應(yīng)一個(gè)表的策略使得該映射策略極易擴(kuò)展,并且能夠?qū)⒁粋€(gè)復(fù)雜的數(shù)據(jù)表轉(zhuǎn)化成若干簡單的值對(duì)象來表示,提高了系統(tǒng)的可維護(hù)性和可修改性。
3) 數(shù)據(jù)訪問對(duì)象(DAO)
根據(jù)數(shù)據(jù)源不同,數(shù)據(jù)訪問也不同。根據(jù)存儲(chǔ)的類型(關(guān)系數(shù)據(jù)庫、面向?qū)ο髷?shù)據(jù)庫等)和供應(yīng)商不同,持久性存儲(chǔ)(比如數(shù)據(jù)庫)的訪問差別也很大。當(dāng)業(yè)務(wù)組件或表示組件需要訪問某數(shù)據(jù)源時(shí),它們可以使用合適的API來獲得連接性,以及操作該數(shù)據(jù)源。但是在這些組件中包含連接性和數(shù)據(jù)訪問代碼會(huì)引入這些組件及數(shù)據(jù)源實(shí)現(xiàn)之間的緊密耦合。組件中這類代碼依賴性使應(yīng)用程序從某種數(shù)據(jù)源遷移到其它種類的數(shù)據(jù)源將變得非常麻煩和困難,當(dāng)數(shù)據(jù)源變化時(shí),組件也需要改變,以便于能夠處理新類型的數(shù)據(jù)源。
筆者開發(fā)項(xiàng)目的數(shù)據(jù)持久層使用數(shù)據(jù)訪問對(duì)象(DAO)來抽象和封裝所有對(duì)數(shù)據(jù)源的訪問。DAO管理著與數(shù)據(jù)源的連接以便于檢索和存儲(chǔ)數(shù)據(jù),DAO實(shí)現(xiàn)了用來操作數(shù)據(jù)源的訪問機(jī)制,內(nèi)部封裝了對(duì)Hibenernate數(shù)據(jù)操縱、事務(wù)處理、會(huì)話管理等API的封裝。外界依賴于DAO的業(yè)務(wù)組件為其客戶端使用DAO提供了更簡單的接口,DAO完全向客戶端隱藏了數(shù)據(jù)源實(shí)現(xiàn)細(xì)節(jié)。由于當(dāng)?shù)蛯訑?shù)據(jù)源實(shí)現(xiàn)變化時(shí),DAO向客戶端提供的接口不會(huì)變化,采用該設(shè)計(jì)模式允許DAO調(diào)整到不同的存儲(chǔ)模式,而不會(huì)影響其客戶端或業(yè)務(wù)組件,即使將來不再采用Hibernate作為關(guān)系映射框架,上層客戶端也不會(huì)受到任何影響。另外,DAO還充當(dāng)組件和數(shù)據(jù)源之間的適配器的角色。
數(shù)據(jù)持久層通過調(diào)整抽象工廠(Abstract Factory)模式和工廠方法(Factory Method) 模式(這二個(gè)創(chuàng)建型模式的實(shí)現(xiàn)詳情參見GoF的<設(shè)計(jì)模式>),),使DAO模式達(dá)到了很高的靈活度。
當(dāng)?shù)讓哟鎯?chǔ)隨著實(shí)現(xiàn)的變化而變化時(shí),該策略可以通過使用抽象工廠模式實(shí)現(xiàn)。抽象工廠可以基于工廠方法實(shí)現(xiàn)而創(chuàng)建,并可使用工廠方法實(shí)現(xiàn)。該策略提供一個(gè)DAO的抽象工廠對(duì)象,其中該對(duì)象可以構(gòu)造多種類型的具體的DAO工廠,每個(gè)工廠支持一種不同類型的持久性存儲(chǔ)實(shí)現(xiàn)。一旦你獲取某特定實(shí)現(xiàn)的具體DAO工廠,可以使用它來生成該實(shí)現(xiàn)中所支持和實(shí)現(xiàn)的DAO。
4) 連接池、應(yīng)用級(jí)緩存及享元模式(提升系統(tǒng)性能)
¨ 緩存(Cache)
對(duì)于數(shù)據(jù)庫來說,廠商的做法往往是在內(nèi)存中開辟相應(yīng)的區(qū)域來存儲(chǔ)可能被多次存取的 數(shù)據(jù)和可能被多次執(zhí)行的語句,以使這些數(shù)據(jù)在下次被訪問時(shí)不必再次提交對(duì)DBMS的請(qǐng)求和那些語句在下次執(zhí)行時(shí)不必再次編譯。
同樣,數(shù)據(jù)持久層采用緩存技術(shù)來保存已經(jīng)從數(shù)據(jù)庫中檢索出來的部分常用數(shù)據(jù)。客戶端訪問持久層時(shí),持久層將首先訪問緩存,如果能夠命中則直接從緩存中提取數(shù)據(jù),否則再向數(shù)據(jù)庫發(fā)送提取數(shù)據(jù)的指令。這種設(shè)計(jì)能夠大幅度地提高數(shù)據(jù)訪問速度。
連接池(Connection Pool)
池是一個(gè)很普遍的概念,和緩沖存儲(chǔ)有機(jī)制相近的地方,都是縮減了訪問的環(huán)節(jié),但它更注重于資源的共享。
對(duì)于訪問數(shù)據(jù)庫來說,建立連接的代價(jià)比較昂貴,因此,數(shù)據(jù)持久層建立了“連接池”以提高訪問的性能。數(shù)據(jù)持久層把連接當(dāng)作對(duì)象,整個(gè)系統(tǒng)啟動(dòng)后,連接池首先建立若干連接,訪問本來需要與數(shù)據(jù)庫連接的區(qū)域,都改為和池相連,池臨時(shí)分配連接供訪問使用,結(jié)果返回后,訪問將連接交還。這種設(shè)計(jì)消除了JDBC與數(shù)據(jù)源建立連接的延時(shí),同時(shí)在應(yīng)用級(jí)提供了對(duì)數(shù)據(jù)源的并發(fā)訪問。
享元模式(Flyweight)
面向?qū)ο笳Z言的原則就是一切都是對(duì)象,但是如果真正使用起來,有時(shí)對(duì)象數(shù)可能顯得很龐大,比如,數(shù)據(jù)庫中的記錄,如果以每條記錄作為一個(gè)對(duì)象,提取幾千條記錄,對(duì)象數(shù)就是幾千,這無疑相當(dāng)耗費(fèi)內(nèi)存。數(shù)據(jù)持久層依據(jù)享元模式設(shè)計(jì)了若干元類,封裝可以被共享的類。這種設(shè)計(jì)策略顯著降低了系統(tǒng)的內(nèi)存消耗。
5)各種對(duì)象的創(chuàng)建模式—工廠方法(Factory Method)
工廠方法模式將創(chuàng)建實(shí)例的工作與使用實(shí)例的工作分開,也就是說,讓創(chuàng)建實(shí)例所需要的大量初始化工作從簡單的構(gòu)造函數(shù)中分離出去。只需要調(diào)用一個(gè)統(tǒng)一的方法,即可根據(jù)需要?jiǎng)?chuàng)建出各種對(duì)象的實(shí)例,對(duì)象的創(chuàng)建方法不再用編碼到程序模塊中,而是統(tǒng)一編寫在工廠類中。這樣在系統(tǒng)進(jìn)行擴(kuò)充修改時(shí),系統(tǒng)的變化僅存在于工廠類內(nèi)部,而絕對(duì)不會(huì)對(duì)其他對(duì)象造成影響。
【編輯推薦】