陳道遠(yuǎn)
摘 要:DAO模式是標(biāo)準(zhǔn)的J2EE設(shè)計(jì)模式之一,是持久層的標(biāo)準(zhǔn)模式。實(shí)現(xiàn)DAO模式不但屏蔽了數(shù)據(jù)存儲(chǔ)最終介質(zhì)的不同,也屏蔽了實(shí)現(xiàn)技術(shù)的不同。分析了傳統(tǒng)DAO模式設(shè)計(jì),闡述了泛型程序設(shè)計(jì)和JPA規(guī)范,提出了一種將泛型程序設(shè)計(jì)以及JPA規(guī)范引入DAO模式的設(shè)計(jì)思路,給出了在Spring框架中基于JPA規(guī)范實(shí)現(xiàn)通用泛型DAO的方法。
關(guān)鍵詞:DAO模式;JPA規(guī)范;泛型程序設(shè)計(jì)
DOIDOI:10.11907/rjdk.162179
中圖分類號(hào):TP301
文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1672-7800(2016)012-0011-03
0 引言
DAO(Data Acess Object)模式是標(biāo)準(zhǔn)的J2EE設(shè)計(jì)模式之一,開發(fā)人員使用這個(gè)模式把底層數(shù)據(jù)訪問操作和上層業(yè)務(wù)邏輯分開。泛型是JDK1.5的新特性,使用泛型機(jī)制編寫的程序代碼要比那些雜亂使用Object變量再進(jìn)行強(qiáng)制類型轉(zhuǎn)換的代碼安全性和可讀性更好。JPA規(guī)范能夠簡化現(xiàn)有Java EE和Java SE應(yīng)用的對(duì)象持久化開發(fā)工作,整合ORM(Object Relational Mapping) 對(duì)象關(guān)系映射技術(shù)。
實(shí)際應(yīng)用中,開發(fā)者一般在框架提供的支持類基礎(chǔ)上編寫自己的DAO基類。本文提出了一種在Spring框架中基于JPA規(guī)范的通用泛型DAO模式設(shè)計(jì)方法。
1 DAO模式設(shè)計(jì)泛型引入
1.1 傳統(tǒng)DAO模式設(shè)計(jì)
DAO模式已經(jīng)成為持久層的標(biāo)準(zhǔn)模式,它的靈活性首先要?dú)w功于一個(gè)對(duì)象設(shè)計(jì)的最佳實(shí)踐:針對(duì)接口編程[1]。這一原則規(guī)定實(shí)體必須實(shí)現(xiàn)一個(gè)調(diào)用程序而不是實(shí)體自身使用的接口。因此,可以輕松替換不同的實(shí)現(xiàn)而對(duì)客戶端代碼產(chǎn)生很小的影響,如圖1所示。
圖1是一個(gè)典型的DAO應(yīng)用實(shí)例。在UserDAO中定義訪問User數(shù)據(jù)對(duì)象的接口方法,業(yè)務(wù)層通過接口UserDAO操作數(shù)據(jù),并使用持久化技術(shù)實(shí)現(xiàn)UserDAO接口方法,比如Jdbc、Hibernate等的具體實(shí)現(xiàn),這樣業(yè)務(wù)層和具體持久化技術(shù)就實(shí)現(xiàn)了解耦。
1.2 泛型程序設(shè)計(jì)
Java5.0的新特性之一是引入了泛型[3],泛型允許編寫可作用于任意類型的類,但是直到聲明了類的實(shí)例,才指定特定的類型。因?yàn)榇祟愋筒皇亲鳛轭惗x的一部分而指定的,所以該類一般對(duì)任意指定類型起作用。
大量使用泛型的例子是集合類。例如ArrayList可以聲明只容納String,這樣就增加了類型安全性。
1.3 將泛型引入DAO模式
雖然Spring通過模板和支持類為各種ORM框架提供了出色支持,但在開發(fā)過程中發(fā)現(xiàn)存在不足。首先傳統(tǒng)DAO模式代碼經(jīng)常出現(xiàn)重復(fù),開發(fā)效率不高;其次,由于支持類的一些方法使代碼顯得比較拖沓;第三,在沒有泛型支持的情況下,不但要指定主鍵值還要指定實(shí)體類型,調(diào)用者還需要對(duì)返回結(jié)果對(duì)象強(qiáng)制進(jìn)行類型轉(zhuǎn)換。這些缺點(diǎn)都可通過在基類中引入泛型得到徹底解決。
構(gòu)建泛型DAO是類型安全、代碼精簡的設(shè)計(jì)模式(相對(duì)于傳統(tǒng)DAO),尤其在DAO組件數(shù)量龐大時(shí),代碼量的減少更加明顯,它對(duì)項(xiàng)目成本、時(shí)間安排和工作產(chǎn)生明顯的負(fù)面影響。實(shí)際應(yīng)用中,在Spring支持類的基礎(chǔ)上利用泛型編程編寫自己的DAO基類,進(jìn)行自封裝,可以達(dá)到改進(jìn)DAO模式設(shè)計(jì)的目的。
2 數(shù)據(jù)庫訪問中JPA規(guī)范的運(yùn)用
2.1 JPA規(guī)范概述
JPA(Java Persistence API)是Java EE新的持久化標(biāo)準(zhǔn)規(guī)范[4],它充分借鑒、吸收現(xiàn)有ORM產(chǎn)品和框架,將基于POJO(Plain Old Java Object)簡單的Java對(duì)象模型ORM技術(shù)標(biāo)準(zhǔn)化,成為在J2EE和J2SE環(huán)境中通用的Java持久化API。
2.2 JPA技術(shù)規(guī)范
JPA通過JDK注解或XML描述對(duì)象——關(guān)系表的映射關(guān)系,將運(yùn)行期的實(shí)體對(duì)象持久化到數(shù)據(jù)庫中。JPA包括以下3方面技術(shù):①ORM映射元數(shù)據(jù)。JPA支持兩種形式的元數(shù)據(jù),分別是JDK注解和XML形式;②JPA 的體系結(jié)構(gòu)及API。運(yùn)用JPA訪問數(shù)據(jù)庫時(shí),首先利用EnitityManager工廠類創(chuàng)建EntityManager,然后開始事務(wù)進(jìn)行持久化操作,最后提交和關(guān)閉EntityManager;③查詢語言。JPA 獨(dú)特的 JPQL(Java Persistence Query Language)是 EJB QL 的擴(kuò)展,它是針對(duì)實(shí)體的一種查詢語言。
2.3 Spring框架的JPA支持類
實(shí)際開發(fā)中可以使用Spring的JpaDaoSupport,它為JpaTemplate模板提供了支持類,為使用JPA的DAO提供了方便??梢栽O(shè)置實(shí)體管理器工廠,通過它獲取JpaTemplate模板實(shí)例,并調(diào)用相應(yīng)方法進(jìn)行數(shù)據(jù)訪問和持久化工作。
3 基于JPA規(guī)范的泛型DAO實(shí)現(xiàn)
實(shí)際開發(fā)中,將DAO基類的設(shè)計(jì)基于JPA規(guī)范,并和泛型編程結(jié)合,構(gòu)建一個(gè)通用的DAO層。
3.1 相關(guān)配置
集成JPA規(guī)范需要編寫相應(yīng)的配置信息,主要包括3個(gè)部分:①在Spring框架中通過LocalEntityManagerFactoryBean支持類來配置一個(gè)EnityManagerFactory Bean;②系統(tǒng)需要在META-INF/persistence.xml文件中配置JPA持久化環(huán)境;③必須提供實(shí)體的元數(shù)據(jù)信息。
在Spring的配置文件applicationContext.xml中,需要配置持久化單元:
上述代碼中StrongWEBPU表示一個(gè)持久化單元名稱,容器啟動(dòng)時(shí)根據(jù)persistence.xml文件中定義的JPA環(huán)境信息建好實(shí)例,生成有關(guān)對(duì)象。
配置時(shí)采用Hibernate實(shí)現(xiàn)JPA規(guī)范[5],也可以換成其它形式的ORM層框架。
3.2 通用基礎(chǔ)接口設(shè)計(jì)
一般的實(shí)體都必須擁有CRUD等常規(guī)操作,可以提供一個(gè)通用的DAO接口,避免在每個(gè)實(shí)體DAO接口中重復(fù)定義這些方法。實(shí)體DAO接口可以繼承這個(gè)通用DAO接口,并可以定義實(shí)體類相關(guān)的其它操作方法,對(duì)于不常用的方法則要求子類顯式調(diào)用模板實(shí)例完成。DAO基類部分代碼如下:
public interface IBaseDao
//泛型支持,可加載不同實(shí)體
{
public void save(T transientInstance);
public T update(T detachedInstance);
public void delete(T persistentInstance);
public void deleteAll();
public List
//返回泛型集合的結(jié)果
public Integer getCountAll();
public List findByJPQL(final String strJPQL);
public List findByJPQL(final int intBegin,final int intMax,final String strJPQL,final Object...objParams);
//構(gòu)建復(fù)雜的多條件查詢語句
………
}
實(shí)體類開發(fā)時(shí),需要定義相應(yīng)接口,可繼承基礎(chǔ)接口,只需加入實(shí)體特定的成員變量和方法。假設(shè)一個(gè)用戶(User)實(shí)體接口代碼如下:
public interface IUserDAO extends IBaseDAO
public List
//實(shí)體特有的方法 ………
}
可以看出,它不需要再定義CRUD等常見方法,只需要增加一些個(gè)性化的方法。
3.3 DAO基類設(shè)計(jì)
DAO基類繼承于JpaDaoSupport,并實(shí)現(xiàn)通用基礎(chǔ)接口。此外,還可以在該類中提供更多方便的操作方法降低子類的實(shí)現(xiàn)難度。這樣,子類僅需要指定泛型對(duì)應(yīng)的實(shí)體類就擁有了各種通用的數(shù)據(jù)操作能力,通過反射機(jī)制獲取泛型對(duì)應(yīng)的實(shí)體類型。如果實(shí)體DAO的數(shù)據(jù)操作僅是一些常見的CRUD操作,子類甚至可以不編寫任何代碼,使編碼效率得到極大提高,抽象性上升了一個(gè)臺(tái)階。
示例代碼如下:
public class BaseDAO
//繼承JPA規(guī)范的DAO支持類,實(shí)現(xiàn)泛型DAO接口
{
private Class
//實(shí)體類的類型
public StrongDAO()
//通過反射機(jī)制獲取泛型對(duì)應(yīng)的實(shí)體類的類型
{
this.persistentClass=(Class
}
public void save(T transientInstance){
Assert.notNull(transientInstance);
try {
//使用JpaTemPlate模板類的方法
getJpaTemplate().persist(transientInstance); } catch (RuntimeException re) {
throw re;
} }
public void delete(T persistentInstance) {
Assert.notNull(persistentInstance);
try {
//使用JpaTemPlate模板類的方法
getJpaTemplate().remove(persistentInstance); } catch (RuntimeException re) {
throw re; } }
public Integer executeByJPQL(final String strJPQL)
//使用回調(diào)方法獲取更多的方法{
Object ret = this.getJpaTemplate().execute(new JpaCallback() {
public Object doInJpa(EntityManager em) throws PersistenceException {
Query query = em.createQuery(strJPQL);
return query.executeUpdate();
}
});
return (Integer) ret;
} ………
}
由上述代碼可以看出,DAO基類通過反射機(jī)制可以獲取不同的實(shí)體類型,擁有各種通用的數(shù)據(jù)操作能力,利用Spring支持的JpaDaoSupport 可實(shí)現(xiàn)各種數(shù)據(jù)操作方法。
在實(shí)體類DAO開發(fā)時(shí),通過擴(kuò)展DAO基類,指定具體的實(shí)體類,就將自動(dòng)擁有常見數(shù)據(jù)操作功能。比如,用戶類DAO的實(shí)現(xiàn)可以簡化到少數(shù)幾行代碼:
public class UserDAO extends BaseDAO
public List
//實(shí)體特有的方法,其它方法來自基類
return findByProperty(“name”,xxBy1);
}
通過擴(kuò)展基類,并借助泛型指定實(shí)體類,立即擁有了對(duì)實(shí)例進(jìn)行CRUD操作的功能,且這些功能都是泛型版本的。按照相似方法,可以對(duì)其它不同的實(shí)體進(jìn)行相應(yīng)設(shè)計(jì)。引入JpaDaoSupport能夠使用基于JPA規(guī)范的ORM層框架,可擴(kuò)展性強(qiáng),在變更ORM層的具體實(shí)現(xiàn)時(shí),只需更改相應(yīng)配置文件即可。
4 結(jié)語
本文利用泛型和JPA規(guī)范建立了通用的DAO基類,能夠大大減少DAO層代碼總量,提高了項(xiàng)目開發(fā)效率。
在基礎(chǔ)DAO類中實(shí)現(xiàn)了CRUD等常用方法,還可以抽象出其它常用方法,如通用的翻頁方法等,使其發(fā)揮更大的作用。但這種通用DAO模式也存在不足:
(1)這種模式涉及框架比較多,需要進(jìn)行相關(guān)配置。當(dāng)項(xiàng)目比較小的情況下,可以直接用傳統(tǒng)的DAO設(shè)計(jì)模式,不使用泛型和JPA規(guī)范,開發(fā)效率會(huì)更高。
(2)在開發(fā)中對(duì)JPQL查詢語句沒有進(jìn)行更多優(yōu)化,在大數(shù)據(jù)量操作時(shí)需要提高運(yùn)行效率。
DAO層設(shè)計(jì)是項(xiàng)目開發(fā)的重點(diǎn),然而構(gòu)建一個(gè)通用泛型DAO在不同的項(xiàng)目中有不同的實(shí)現(xiàn),只能抽象出一些共同方法,如通用的翻頁方法等,面對(duì)不同的項(xiàng)目給出各自的實(shí)現(xiàn)。今后要增加一些共同方法,優(yōu)化基類中JPQL語句,使基類發(fā)揮出更大作用。
參考文獻(xiàn):
[1] 計(jì)文柯.Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理[M].北京:機(jī)械工業(yè)出版社,2012.
[2] 陳雄華.精通Spring 2.x:企業(yè)應(yīng)用開發(fā)詳解[M].北京: 電子工業(yè)出版社, 2007.
[3] 吳亞峰,紀(jì)超.Java SE 6.0 編程指南[M].北京:人民郵電出版社,2007.
[4] 王衛(wèi)軍,楚寧志,呂軍.Java軟件體系結(jié)構(gòu)設(shè)計(jì)模式標(biāo)準(zhǔn)指南[M].北京:電子工業(yè)出版社,2006.
[5] 白廣元.Java Web 整合開發(fā)完全自學(xué)手冊(cè)[M].北京:機(jī)械工業(yè)出版社,2009.
(責(zé)任編輯:杜能鋼)