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