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