歐陽宏基,張琳娜,葛 萌
目前企業(yè)級(jí)Java EE應(yīng)用中普遍采用分層的開發(fā)方式,這些分層自頂向下依次為表示層、控制層、業(yè)務(wù)邏輯層、數(shù)據(jù)持久化層和數(shù)據(jù)庫層。分層的優(yōu)勢除了提高開發(fā)效率、層與層之間的松散耦合外,最重要的就是增強(qiáng)組件封裝的能力,從而提高重用性、擴(kuò)展性和可維護(hù)性。其中數(shù)據(jù)持久層的主要功能是為實(shí)體類提供訪問數(shù)據(jù)存儲(chǔ)設(shè)備的接口,開發(fā)中普遍采用了DAO設(shè)計(jì)模式。這樣做的目的是為業(yè)務(wù)邏輯層提供一個(gè)訪問數(shù)據(jù)源的統(tǒng)一接口并隱藏操作數(shù)據(jù)源的實(shí)現(xiàn)細(xì)節(jié)。DAO中基本都是對(duì)實(shí)體類進(jìn)行數(shù)據(jù)源的增刪改查(CRUD)操作,這些操作具有相似性。如果一個(gè)系統(tǒng)中存在著多個(gè)實(shí)體類,那么就會(huì)產(chǎn)生大量的重復(fù)代碼并且有潛在的類型轉(zhuǎn)換風(fēng)險(xiǎn),不利于系統(tǒng)的維護(hù)。為了解決這個(gè)問題,將泛型機(jī)制引入到DAO模式中并結(jié)合模板模式,從而避免了大量的重復(fù)代碼并且解決了強(qiáng)制類型轉(zhuǎn)換所帶來的風(fēng)險(xiǎn)。
DAO(Data Access Object)模式是將業(yè)務(wù)邏輯從數(shù)據(jù)存取邏輯中分離出來,使得業(yè)務(wù)邏輯不會(huì)因?yàn)閿?shù)據(jù)源的改變而改變,如圖1所示:
圖1 DAO模式
DAO模式包括四部分:業(yè)務(wù)對(duì)象、數(shù)據(jù)訪問對(duì)象、值對(duì)象(實(shí)體類對(duì)象)和數(shù)據(jù)源。業(yè)務(wù)對(duì)象并不直接和數(shù)據(jù)源交互,而是通過DAO提供的接口獲得值對(duì)象或者實(shí)體類對(duì)象,修改他們的值后通過DAO保存到數(shù)據(jù)源。某些情況下值對(duì)象中的屬性是實(shí)體類對(duì)象屬性的一個(gè)子集,通常會(huì)根據(jù)應(yīng)用的具體情況而決定使用值對(duì)象還是實(shí)體類對(duì)象。業(yè)務(wù)邏輯僅僅通過面向?qū)ο蟮姆椒ú僮鱀AO,無需考慮數(shù)據(jù)源的具體類型、事務(wù)、并發(fā)等復(fù)雜問題[1]。
DAO模式完成了一個(gè)持久層的部分任務(wù),向業(yè)務(wù)邏輯開發(fā)人員隱藏了對(duì)象的持久化細(xì)節(jié)。不過DAO本身并不執(zhí)行真正意義上的持久化操作,需要交給JDBC、ORM框架或者JPA等API來完成。
Java中的類型轉(zhuǎn)換分為上轉(zhuǎn)型和下轉(zhuǎn)型兩種情況,上轉(zhuǎn)型是自動(dòng)轉(zhuǎn)型也就是說父類的引用可以指向一個(gè)子類的實(shí)例(接口及其實(shí)現(xiàn)類也適用于這種情況),但這種情況子類對(duì)象就失去了自己的“特性”;下轉(zhuǎn)型是強(qiáng)制類型轉(zhuǎn)換,通常用于將上轉(zhuǎn)型對(duì)象轉(zhuǎn)換為一個(gè)特定類型的子類對(duì)象,在這種情況下就必須知道子類的類型否則就會(huì)出現(xiàn)轉(zhuǎn)換異常。這個(gè)問題在泛型中得到了解決。
泛型機(jī)制自Java SE 5.0以后引入,使得相同的代碼可以應(yīng)用于多種類型,其目的是希望類或方法能夠具備最廣泛的表達(dá)能力[2]。泛型對(duì)于集合類非常有用,相比于對(duì)Object類型的變量進(jìn)行強(qiáng)制轉(zhuǎn)換操作,通過泛型的類型參數(shù)可以明確指定加入到集合中的對(duì)象類型,將來從集合中取出對(duì)象時(shí)也就不必強(qiáng)制轉(zhuǎn)換了,從而使代碼具有清晰的可讀性和安全性。
傳統(tǒng)的DAO與實(shí)體類一一對(duì)應(yīng),幾乎每個(gè)實(shí)體類都要經(jīng)過對(duì)數(shù)據(jù)源的CRUD操作,這些操作具有相同點(diǎn)。定義泛型DAO就是要將相同部分的CRUD操作提取出來,如圖2所示:
圖2 泛型DAO類圖關(guān)系
其中IGenericDAO是一個(gè)通用的泛型DAO接口,定義了幾乎所有持久化類都需要的CRUD操作。由于采用JDBC對(duì)數(shù)據(jù)庫執(zhí)行增、刪、改操作都需要調(diào)用Statement或PreparedStatement對(duì)象的executeUpdate()方法,所以將增、刪、改操作統(tǒng)一由update()方法來定義。AbstractGeneric DAOImp這個(gè)抽象類是對(duì)IGenericDAO的一個(gè)基本實(shí)現(xiàn),該類采用了模板設(shè)計(jì)模式,抽象方法rowMapper()交給子類去實(shí)現(xiàn),用來完成ResultSet對(duì)象與實(shí)體類的映射關(guān)系。IConcreteEntityDAO是具體實(shí)體類所對(duì)應(yīng)的DAO接口,其中可以定義該實(shí)體類特有的CRUD操作。ConcreteEntity DAOImp是具體實(shí)體類的DAO實(shí)現(xiàn)類。
基于泛型DAO的持久層模型包括4部分,如圖3所示:
圖3 數(shù)據(jù)持久化層模型
分別為:DAO工廠組件、泛型DAO組件、持久化操作組件和實(shí)體類組件。通過組件之間的劃分,既可以明確職責(zé)又可以降低框架各部分之間的耦合程度,方便開發(fā)人員的分工協(xié)作。各部分組件的功能如下說明:
(1)DAO工廠組件:業(yè)務(wù)邏輯組件的數(shù)據(jù)請求都由DAO工廠類完成。DAO工廠類采用的是對(duì)象組合機(jī)制,通過DAO工廠類和DAO接口,向業(yè)務(wù)邏輯組件隱藏具體DAO實(shí)現(xiàn)類的創(chuàng)建過程。
(2)泛型DAO組件:每個(gè)實(shí)體類對(duì)應(yīng)的DAO組件如圖2所示,根據(jù)該實(shí)體類的具體情況可以再定義新的持久化方法。DAO組件由DAOFactory類創(chuàng)建。
(3)持久化操作組件:直接操作數(shù)據(jù)庫的組件。能夠?qū)崿F(xiàn)某個(gè)具體實(shí)體類的CRUD操作和處理數(shù)據(jù)存取異常;負(fù)責(zé)數(shù)據(jù)庫連接池的建立與管理以及返回值對(duì)象等。
(4)實(shí)體類組件:它是由普通Java對(duì)象(POJO)組成,用來在層與層之間傳遞數(shù)據(jù),有些情況下也可以當(dāng)做值對(duì)象來使用。每個(gè)POJO對(duì)象都包含了若干屬性以及設(shè)置和獲取這些屬性的set()和get()方法,這些屬性通常要與數(shù)據(jù)庫表形成對(duì)應(yīng)關(guān)系。
本節(jié)以陜西省某礦業(yè)集團(tuán)計(jì)劃部門的生產(chǎn)統(tǒng)計(jì)管理系統(tǒng)為背景,詳細(xì)介紹2.3節(jié)所提出的數(shù)據(jù)持久化層中泛型DAO組件的實(shí)現(xiàn)過程。該系統(tǒng)主要是為了讓計(jì)劃部門利用計(jì)算機(jī)技術(shù)對(duì)所管轄的各分部門產(chǎn)生的實(shí)際數(shù)據(jù)進(jìn)行統(tǒng)一管理,以便及時(shí)調(diào)整計(jì)劃數(shù)據(jù),從而為企業(yè)的決策提供支持。系統(tǒng)主要包括:礦區(qū)主要經(jīng)濟(jì)指標(biāo)管理、掘進(jìn)工作面管理、進(jìn)尺計(jì)劃完成管理、回采工作面管理、鐵路運(yùn)輸完成管理、礦區(qū)從業(yè)人員管理、礦區(qū)企事業(yè)人員管理、礦井期末3個(gè)煤量管理、礦井安全生產(chǎn)管理、機(jī)電設(shè)備完好及影響、分項(xiàng)原煤成本管理、損益構(gòu)成情況管理、經(jīng)營利潤實(shí)現(xiàn)管理和商品煤銷售管理等14個(gè)管理模塊[3]。
數(shù)據(jù)持久層組件是由業(yè)務(wù)邏輯層調(diào)用的,調(diào)用順序如下[4]:
(1)業(yè)務(wù)邏輯組件向DAOFactory發(fā)出請求。
(2)DAOFactory類根據(jù)請求,通過泛型DAO接口去創(chuàng)建相應(yīng)的DAO實(shí)現(xiàn)類。
(3)DAO實(shí)現(xiàn)類通過JdbcUtil從數(shù)據(jù)庫連接池獲取Connection對(duì)象,完成具體的CRUD操作,將操作結(jié)果封裝到實(shí)體類中。
(4)DAO類將處理結(jié)果通過DAOFactory類以值對(duì)象形式返回給業(yè)務(wù)邏輯組件。
3.2.1 通用泛型DAO接口的定義
該泛型接口中定義的是系統(tǒng)中所有實(shí)體類所具有的公共CRUD操作,T表示任意的實(shí)體類型,ID表示實(shí)體類型對(duì)應(yīng)的數(shù)據(jù)表主鍵。
3.2.2 通用泛型DAO接口的抽象實(shí)現(xiàn)
利用JDBC訪問數(shù)據(jù)庫通常都要執(zhí)行以下操作:①加載數(shù)據(jù)庫驅(qū)動(dòng)②創(chuàng)建Connection數(shù)據(jù)庫連接對(duì)象③創(chuàng)建執(zhí)行SQL語句的Statement或PreparedStatement對(duì)象④釋放連接等資源。其中①②④這3個(gè)步驟基本是固定不變的,由JdbcUtil這個(gè)工具類進(jìn)行了封裝。第③步操作是可變的,需要根據(jù)SQL的類型進(jìn)行判斷(大多數(shù)情況執(zhí)行的都是數(shù)據(jù)庫的DDL語句),如果是查詢語句則需要將結(jié)果集與實(shí)體類的映射關(guān)系交給子類去處理。這種情況符合模板設(shè)計(jì)模式的思想:可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟,這些特定步驟延遲到子類中進(jìn)行定義[5]。因此AbstractGenericDAOImp類采用模板模式進(jìn)行設(shè)計(jì),其中可變的部分用rowMapper方法定義,具體實(shí)現(xiàn)過程交給子類完成。該類的相關(guān)核心代碼如下所示:
3.2.3 實(shí)體類所對(duì)應(yīng)的DAO接口
具體實(shí)體類所對(duì)應(yīng)的DAO接口需要從IGenericDAO繼承,需要顯示聲明T和ID所對(duì)應(yīng)的類型,并且根據(jù)實(shí)際情況定義該實(shí)體所對(duì)應(yīng)業(yè)務(wù)邏輯需要的CRUD操作。以回采工作面管理模塊為例,需要按年、月、日等時(shí)間查看回采工作面的相關(guān)數(shù)據(jù)。該接口的相關(guān)代碼如下:
3.2.4 實(shí)體類所對(duì)應(yīng)的DAO接口的實(shí)現(xiàn)
實(shí)體類DAO的實(shí)現(xiàn)類繼承抽象父類GenericDAOImp,需要顯示聲明T和ID所對(duì)應(yīng)的類型,這樣在返回實(shí)體類對(duì)象的方法中就無須強(qiáng)制類型轉(zhuǎn)換了。除了實(shí)現(xiàn)接口中定義的方法外,該類要重寫rowMapper方法,用來詳細(xì)描述實(shí)體類中屬性與數(shù)據(jù)庫表的對(duì)應(yīng)關(guān)系。以下是回采工作面DAO實(shí)現(xiàn)類的相關(guān)代碼,以查詢方法為例。
本文基于Java泛型技術(shù)和DAO模式,設(shè)計(jì)了一種泛型DAO數(shù)據(jù)持久化層模型,通過陜西省某礦業(yè)集團(tuán)生產(chǎn)統(tǒng)計(jì)管理系統(tǒng)詳細(xì)描述了該模型組成部分的實(shí)現(xiàn)過程。通過泛型DAO的顯示類型聲明避免了強(qiáng)制類型轉(zhuǎn)化所可能產(chǎn)生的異常風(fēng)險(xiǎn);在泛型DAO的抽象實(shí)現(xiàn)中所采用的模板模式簡化了數(shù)據(jù)庫的CRUD操作避免了在實(shí)體類DAO中出現(xiàn)重復(fù)代碼,也有利于DAO的擴(kuò)展。
[1]孫霞.基于DAO 模式的持久模型的研究與設(shè)計(jì)[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2010,19(7),107-108.
[2]Clay RW,Donald A,Scot S.Professional Java JDK 6 Edition[M].American:Wiley Publishing,2007.
[3]歐陽宏基.基于框架技術(shù)的生產(chǎn)統(tǒng)計(jì)管理系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J].微計(jì)算機(jī)應(yīng)用,2009,30(9),76-77
[4]張俐,張維璽.改進(jìn)的JDBC框架在數(shù)據(jù)持久層的應(yīng)用[J].計(jì)算機(jī)工程與設(shè)計(jì),2010,31(8),1746-1747.
[5]耿祥義,張躍平.Java設(shè)計(jì)模式[M].清華大學(xué)出版社,2009,215-216.