歐陽宏基,張琳娜,葛 萌
目前企業級Java EE應用中普遍采用分層的開發方式,這些分層自頂向下依次為表示層、控制層、業務邏輯層、數據持久化層和數據庫層。分層的優勢除了提高開發效率、層與層之間的松散耦合外,最重要的就是增強組件封裝的能力,從而提高重用性、擴展性和可維護性。其中數據持久層的主要功能是為實體類提供訪問數據存儲設備的接口,開發中普遍采用了DAO設計模式。這樣做的目的是為業務邏輯層提供一個訪問數據源的統一接口并隱藏操作數據源的實現細節。DAO中基本都是對實體類進行數據源的增刪改查(CRUD)操作,這些操作具有相似性。如果一個系統中存在著多個實體類,那么就會產生大量的重復代碼并且有潛在的類型轉換風險,不利于系統的維護。為了解決這個問題,將泛型機制引入到DAO模式中并結合模板模式,從而避免了大量的重復代碼并且解決了強制類型轉換所帶來的風險。
DAO(Data Access Object)模式是將業務邏輯從數據存取邏輯中分離出來,使得業務邏輯不會因為數據源的改變而改變,如圖1所示:

圖1 DAO模式
DAO模式包括四部分:業務對象、數據訪問對象、值對象(實體類對象)和數據源。業務對象并不直接和數據源交互,而是通過DAO提供的接口獲得值對象或者實體類對象,修改他們的值后通過DAO保存到數據源。某些情況下值對象中的屬性是實體類對象屬性的一個子集,通常會根據應用的具體情況而決定使用值對象還是實體類對象。業務邏輯僅僅通過面向對象的方法操作DAO,無需考慮數據源的具體類型、事務、并發等復雜問題[1]。
DAO模式完成了一個持久層的部分任務,向業務邏輯開發人員隱藏了對象的持久化細節。不過DAO本身并不執行真正意義上的持久化操作,需要交給JDBC、ORM框架或者JPA等API來完成。
Java中的類型轉換分為上轉型和下轉型兩種情況,上轉型是自動轉型也就是說父類的引用可以指向一個子類的實例(接口及其實現類也適用于這種情況),但這種情況子類對象就失去了自己的“特性”;下轉型是強制類型轉換,通常用于將上轉型對象轉換為一個特定類型的子類對象,在這種情況下就必須知道子類的類型否則就會出現轉換異常。這個問題在泛型中得到了解決。
泛型機制自Java SE 5.0以后引入,使得相同的代碼可以應用于多種類型,其目的是希望類或方法能夠具備最廣泛的表達能力[2]。泛型對于集合類非常有用,相比于對Object類型的變量進行強制轉換操作,通過泛型的類型參數可以明確指定加入到集合中的對象類型,將來從集合中取出對象時也就不必強制轉換了,從而使代碼具有清晰的可讀性和安全性。
傳統的DAO與實體類一一對應,幾乎每個實體類都要經過對數據源的CRUD操作,這些操作具有相同點。定義泛型DAO就是要將相同部分的CRUD操作提取出來,如圖2所示:

圖2 泛型DAO類圖關系
其中IGenericDAO是一個通用的泛型DAO接口,定義了幾乎所有持久化類都需要的CRUD操作。由于采用JDBC對數據庫執行增、刪、改操作都需要調用Statement或PreparedStatement對象的executeUpdate()方法,所以將增、刪、改操作統一由update()方法來定義。AbstractGeneric DAOImp這個抽象類是對IGenericDAO的一個基本實現,該類采用了模板設計模式,抽象方法rowMapper()交給子類去實現,用來完成ResultSet對象與實體類的映射關系。IConcreteEntityDAO是具體實體類所對應的DAO接口,其中可以定義該實體類特有的CRUD操作。ConcreteEntity DAOImp是具體實體類的DAO實現類。
基于泛型DAO的持久層模型包括4部分,如圖3所示:

圖3 數據持久化層模型
分別為:DAO工廠組件、泛型DAO組件、持久化操作組件和實體類組件。通過組件之間的劃分,既可以明確職責又可以降低框架各部分之間的耦合程度,方便開發人員的分工協作。各部分組件的功能如下說明:
(1)DAO工廠組件:業務邏輯組件的數據請求都由DAO工廠類完成。DAO工廠類采用的是對象組合機制,通過DAO工廠類和DAO接口,向業務邏輯組件隱藏具體DAO實現類的創建過程。
(2)泛型DAO組件:每個實體類對應的DAO組件如圖2所示,根據該實體類的具體情況可以再定義新的持久化方法。DAO組件由DAOFactory類創建。
(3)持久化操作組件:直接操作數據庫的組件。能夠實現某個具體實體類的CRUD操作和處理數據存取異常;負責數據庫連接池的建立與管理以及返回值對象等。
(4)實體類組件:它是由普通Java對象(POJO)組成,用來在層與層之間傳遞數據,有些情況下也可以當做值對象來使用。每個POJO對象都包含了若干屬性以及設置和獲取這些屬性的set()和get()方法,這些屬性通常要與數據庫表形成對應關系。
本節以陜西省某礦業集團計劃部門的生產統計管理系統為背景,詳細介紹2.3節所提出的數據持久化層中泛型DAO組件的實現過程。該系統主要是為了讓計劃部門利用計算機技術對所管轄的各分部門產生的實際數據進行統一管理,以便及時調整計劃數據,從而為企業的決策提供支持。系統主要包括:礦區主要經濟指標管理、掘進工作面管理、進尺計劃完成管理、回采工作面管理、鐵路運輸完成管理、礦區從業人員管理、礦區企事業人員管理、礦井期末3個煤量管理、礦井安全生產管理、機電設備完好及影響、分項原煤成本管理、損益構成情況管理、經營利潤實現管理和商品煤銷售管理等14個管理模塊[3]。
數據持久層組件是由業務邏輯層調用的,調用順序如下[4]:
(1)業務邏輯組件向DAOFactory發出請求。
(2)DAOFactory類根據請求,通過泛型DAO接口去創建相應的DAO實現類。
(3)DAO實現類通過JdbcUtil從數據庫連接池獲取Connection對象,完成具體的CRUD操作,將操作結果封裝到實體類中。
(4)DAO類將處理結果通過DAOFactory類以值對象形式返回給業務邏輯組件。
3.2.1 通用泛型DAO接口的定義
該泛型接口中定義的是系統中所有實體類所具有的公共CRUD操作,T表示任意的實體類型,ID表示實體類型對應的數據表主鍵。

3.2.2 通用泛型DAO接口的抽象實現
利用JDBC訪問數據庫通常都要執行以下操作:①加載數據庫驅動②創建Connection數據庫連接對象③創建執行SQL語句的Statement或PreparedStatement對象④釋放連接等資源。其中①②④這3個步驟基本是固定不變的,由JdbcUtil這個工具類進行了封裝。第③步操作是可變的,需要根據SQL的類型進行判斷(大多數情況執行的都是數據庫的DDL語句),如果是查詢語句則需要將結果集與實體類的映射關系交給子類去處理。這種情況符合模板設計模式的思想:可以不改變一個算法的結構即可重定義該算法的某些特定步驟,這些特定步驟延遲到子類中進行定義[5]。因此AbstractGenericDAOImp類采用模板模式進行設計,其中可變的部分用rowMapper方法定義,具體實現過程交給子類完成。該類的相關核心代碼如下所示:

3.2.3 實體類所對應的DAO接口
具體實體類所對應的DAO接口需要從IGenericDAO繼承,需要顯示聲明T和ID所對應的類型,并且根據實際情況定義該實體所對應業務邏輯需要的CRUD操作。以回采工作面管理模塊為例,需要按年、月、日等時間查看回采工作面的相關數據。該接口的相關代碼如下:


3.2.4 實體類所對應的DAO接口的實現
實體類DAO的實現類繼承抽象父類GenericDAOImp,需要顯示聲明T和ID所對應的類型,這樣在返回實體類對象的方法中就無須強制類型轉換了。除了實現接口中定義的方法外,該類要重寫rowMapper方法,用來詳細描述實體類中屬性與數據庫表的對應關系。以下是回采工作面DAO實現類的相關代碼,以查詢方法為例。

本文基于Java泛型技術和DAO模式,設計了一種泛型DAO數據持久化層模型,通過陜西省某礦業集團生產統計管理系統詳細描述了該模型組成部分的實現過程。通過泛型DAO的顯示類型聲明避免了強制類型轉化所可能產生的異常風險;在泛型DAO的抽象實現中所采用的模板模式簡化了數據庫的CRUD操作避免了在實體類DAO中出現重復代碼,也有利于DAO的擴展。
[1]孫霞.基于DAO 模式的持久模型的研究與設計[J].計算機系統應用,2010,19(7),107-108.
[2]Clay RW,Donald A,Scot S.Professional Java JDK 6 Edition[M].American:Wiley Publishing,2007.
[3]歐陽宏基.基于框架技術的生產統計管理系統設計與實現[J].微計算機應用,2009,30(9),76-77
[4]張俐,張維璽.改進的JDBC框架在數據持久層的應用[J].計算機工程與設計,2010,31(8),1746-1747.
[5]耿祥義,張躍平.Java設計模式[M].清華大學出版社,2009,215-216.