歐陽(yáng)宏基, 葛萌, 程海波
(咸陽(yáng)師范學(xué)院, 計(jì)算機(jī)學(xué)院, 陜西, 咸陽(yáng) 712000)
JDBC是JavaEE平臺(tái)中用來訪問關(guān)系型數(shù)據(jù)庫(kù)的原生API[1],它存在若干缺點(diǎn):頻繁進(jìn)行Connection對(duì)象的創(chuàng)建和銷毀,影響數(shù)據(jù)庫(kù)性能;將SQL語句硬編碼到Java代碼中,導(dǎo)致SQL語句的修改,要重新編譯Java代碼;將PreparedStatement對(duì)象中的占位符參數(shù)硬編碼到Java代碼中,不利于系統(tǒng)維護(hù);從結(jié)果集遍歷數(shù)據(jù)時(shí),需要編寫數(shù)據(jù)表字段與對(duì)象屬性的映射邏輯。
針對(duì)上述缺點(diǎn),本文提出基于MyBatis框架的數(shù)據(jù)庫(kù)持久層設(shè)計(jì)方案。首先分析了MyBatis框架的核心組件與執(zhí)行流程,闡述了MyBatis實(shí)現(xiàn)DAO的兩種方案并分析了優(yōu)缺點(diǎn),結(jié)合目標(biāo)考核管理系統(tǒng)持久層的部分業(yè)務(wù)邏輯詳細(xì)描述了MyBatis的應(yīng)用過程,為數(shù)據(jù)持久層的開發(fā)提供了一定的借鑒。
MyBatis對(duì)JDBC 進(jìn)行了二次封裝,能夠與第三方數(shù)據(jù)庫(kù)連接池集成,實(shí)現(xiàn)Connection對(duì)象的復(fù)用[2]。MyBatis使開發(fā)人員將主要精力放在編寫SQL語句上,通過映射方式,半自動(dòng)化地生成持久化代碼。通過輸入映射自動(dòng)完成向preparedStatement中輸入?yún)?shù),通過輸出映射自動(dòng)將結(jié)果集映射成Java對(duì)象[3],實(shí)現(xiàn)了Java代碼與JDBC API的解耦。使得開發(fā)人員無須花精力去處理諸如注冊(cè)驅(qū)動(dòng)、創(chuàng)建Connection、創(chuàng)建Statement、手動(dòng)設(shè)置參數(shù)、結(jié)果集檢索等繁瑣的操作。
MyBatis的核心組件包括以下部分[4],結(jié)構(gòu)如圖1所示。
圖1 MyBatis框架核心組件
(1) 核心配置文件:它是MyBatis的全局配置文件,主要完成數(shù)據(jù)源、事務(wù)處理、延遲加載、緩存和加載映射文件等配置。
(2) 映射文件(mapper.xml):定義操作數(shù)據(jù)庫(kù)的SQL語句,實(shí)現(xiàn)輸入映射和輸出映射。
(3) SqlSessionFactory(會(huì)話工廠):根據(jù)配置文件由SqlSessionFactoryBuilder創(chuàng)建會(huì)話工廠,從而創(chuàng)建SqlSession對(duì)象。
(4) SqlSession:定義了一組數(shù)據(jù)庫(kù)操作的API。
(5) Executor:它是SqlSession內(nèi)部真正操作數(shù)據(jù)庫(kù)的對(duì)象。根據(jù)SqlSession傳遞的參數(shù)動(dòng)態(tài)地生成需要執(zhí)行的SQL語句,同時(shí)負(fù)責(zé)查詢緩存的維護(hù)。
(6) MappedStatement:對(duì)映射信息進(jìn)行封裝,用于存儲(chǔ)要映射的SQL語句的id、參數(shù)等。
MyBatis的執(zhí)行流程如圖2所示。首先通過流加載核心配置文件,然后將流對(duì)象注入SqlSessionFactoryBuilder對(duì)象用于創(chuàng)建SqlSessionFactory對(duì)象,從SqlSessionFactory 對(duì)象中獲取SqlSession,利用SqlSession的selectOne()、selectList()、insert()、 update()、delete()等方法操作數(shù)據(jù)庫(kù),最后關(guān)閉SqlSession對(duì)象。
圖2 MyBatis框架的執(zhí)行流程
數(shù)據(jù)持久層在Java EE中主要用來為業(yè)務(wù)邏輯層提供訪問數(shù)據(jù)源的接口,封裝操作數(shù)據(jù)源的邏輯和相關(guān)API,完成實(shí)體對(duì)象與數(shù)據(jù)庫(kù)表的映射。普遍采用DAO設(shè)計(jì)模式來封裝數(shù)據(jù)持久層的代碼[5]。所以,MyBatis框架完成數(shù)據(jù)持久層開發(fā)主要就是實(shí)現(xiàn)DAO,如圖3所示。MyBatis提供了兩種實(shí)現(xiàn)DAO的方式:一種是傳統(tǒng)DAO的實(shí)現(xiàn)方式,需要編寫DAO接口、DAO的實(shí)現(xiàn)類、實(shí)體類和映射文件,在DAO實(shí)現(xiàn)類中調(diào)用MyBatis的API操作數(shù)據(jù)庫(kù);另一種實(shí)現(xiàn)方式是采用動(dòng)態(tài)代理技術(shù)自動(dòng)生成DAO 實(shí)現(xiàn)類的對(duì)象,開發(fā)人員只需編寫DAO接口、實(shí)體類和映射文件,不需要編寫DAO的實(shí)現(xiàn)類,通過SqlSession的getMapper()方法就能自動(dòng)生成DAO實(shí)現(xiàn)類的對(duì)象。相比傳統(tǒng)DAO的實(shí)現(xiàn)方式,此方式無需編寫調(diào)用MyBatis API操作數(shù)據(jù)庫(kù)的代碼,能夠提高數(shù)據(jù)持久層的開發(fā)效率。
圖3 MyBatis開發(fā)持久層示意圖
高等院校目標(biāo)考核管理系統(tǒng)是在高校多職能部門的配合下采用信息化手段進(jìn)行人力資源管理的一個(gè)應(yīng)用軟件[6]。其功能模塊包括:基礎(chǔ)數(shù)據(jù)維護(hù)、教工信息管理、指標(biāo)管理、信息查詢、系統(tǒng)維護(hù)等[7]。用戶角色包括:教工、各職能部門管理員、系統(tǒng)管理員、校領(lǐng)導(dǎo)。系統(tǒng)基于MVC模式、采用Java EE分層架構(gòu)模型[8-9],數(shù)據(jù)持久層利用MyBatis框架進(jìn)行設(shè)計(jì)與開發(fā)。
定義mybatis-config.xml作為核心配置文件,利用C3P0數(shù)據(jù)庫(kù)連接池作為數(shù)據(jù)源。首先通過〈properties〉標(biāo)簽的resource屬性加載C3P0.properties文件,該文件定義了數(shù)據(jù)庫(kù)驅(qū)動(dòng)、URL、用戶名、密碼、數(shù)據(jù)庫(kù)連接池配置參數(shù)等信息。然后通過〈environments〉標(biāo)簽定義數(shù)據(jù)源,通過OGNL表達(dá)式讀取C3P0.properties文件中的值。采用JDBC的事務(wù)管理,由MyBatis控制事務(wù)。通過〈settings〉標(biāo)簽進(jìn)行全局參數(shù)配置,包括延遲加載和二級(jí)緩存。通過〈typeAliases〉標(biāo)簽以批量方式為POJO類定義別名。最后通過〈mappers〉標(biāo)簽來加載映射文件。
采用動(dòng)態(tài)代理生成DAO實(shí)現(xiàn)類的方式作為持久層的開發(fā)方法。首先要定義DAO接口(Mapper接口),其中定義與實(shí)體類或角色對(duì)應(yīng)的持久化方法。目標(biāo)考核系統(tǒng)持久層一共定義了TeacherMapper.java、PaperMapper.java、TaskDetailMapper. java等53個(gè)Mapper接口。以系統(tǒng)中校領(lǐng)導(dǎo)角色為例,對(duì)應(yīng)的Mapper接口中與查詢科研數(shù)據(jù)有關(guān)的部分源碼如下:
public interface XLDMapper{
public List〈Papaer〉 findPaper(PaperDTO paperDTO) throws Exception;
public List〈Patent〉 findPatent(PatentDTO patentDTO) throws Exception;
…………
}
定義實(shí)體類與數(shù)據(jù)庫(kù)表進(jìn)行映射,其中的屬性包括兩類:一類是與表字段一一對(duì)應(yīng)的基本屬性;另一類是在聯(lián)合查詢中定義所關(guān)聯(lián)的對(duì)象或?qū)ο蠹稀7椒òㄔO(shè)置和獲取屬性的setter和getter方法。目標(biāo)考核系統(tǒng)中所包含的實(shí)體類有:UserInfo、Teacher、Department、Role、Permission、Paper、ScientificProject、Patent、Monograph、Student、IndexLevelOne、IndexLevelTwo、IndexLevelThree等53個(gè)。
相對(duì)于原生JDBC,MyBatis框架將SQL語句轉(zhuǎn)移到映射文件中定義,實(shí)現(xiàn)了與Java代碼的解耦合。因此,編寫映射文件是使用MyBatis框架的核心內(nèi)容。映射文件中主要定義與Mapper接口中方法一一對(duì)應(yīng)的Statement,主要包括Statement ID、SQL語句、輸入映射和輸出映射。其中,〈insert〉、〈delete〉、〈update〉和〈select〉標(biāo)簽分別定義數(shù)據(jù)庫(kù)的增、刪、改和查詢操作。輸入映射定義對(duì)應(yīng)SQL語句所需要的參數(shù),輸出映射定義SQL語句所返回的數(shù)據(jù)類型。MyBatis在執(zhí)行數(shù)據(jù)庫(kù)操作時(shí)會(huì)把Statement解析成一條具體的SQL,將輸入映射類型中的數(shù)據(jù)替換SQL語句中的?占位符,將執(zhí)行結(jié)果轉(zhuǎn)換成輸出映射對(duì)應(yīng)的Java數(shù)據(jù)類型。
以XLDMapper.xml為例,它是XLDMapper.java接口對(duì)應(yīng)的映射文件。其中,findPaper()方法的邏輯是按照年份、學(xué)院、職稱等條件相組合來查詢發(fā)表的論文數(shù)據(jù)。PapaerDTO表示論文信息的數(shù)據(jù)傳輸對(duì)象,是輸入映射的POJO類型,該類封裝了查詢語句需要的參數(shù)信息,具體包括UserInfo實(shí)體類、Department實(shí)體類和年份。執(zhí)行結(jié)果轉(zhuǎn)換成Paper類型,它是論文信息的實(shí)體類??紤]到用戶在頁(yè)面所選查詢條件的不同,需要根據(jù)不同查詢條件來動(dòng)態(tài)拼接SQL語句。
持久層的Mapper接口和映射文件定義完成后需要在JUnit中進(jìn)行單體測(cè)試,確保邏輯正確后才能供業(yè)務(wù)層組件調(diào)用。將MyBatis操作數(shù)據(jù)庫(kù)的共性步驟封裝到工具類中,這樣可以避免冗余代碼。定義MyBatisUtil工具類將加載配置文件、創(chuàng)建SqlSessionFactory、創(chuàng)建SqlSession、釋放資源等操作進(jìn)行封裝,并且確保SqlSessionFactory是一個(gè)單例對(duì)象[10]。
數(shù)據(jù)持久層在Java EE分層架構(gòu)中占據(jù)著舉足輕重的地位,穩(wěn)定、高效的持久層開發(fā)方案一直是JavaEE應(yīng)用開發(fā)的重點(diǎn)。本文提出了基于MyBatis框架的數(shù)據(jù)持久層應(yīng)用開發(fā)方法,并成功應(yīng)用到目標(biāo)考核管理系統(tǒng)數(shù)據(jù)持久層的設(shè)計(jì)與實(shí)現(xiàn)中。MyBatis框架通過映射文件分離了Java代碼與SQL語句,通過輸入映射、輸出映射以及DAO的動(dòng)態(tài)生成等功能簡(jiǎn)化了代碼編寫量,提高了數(shù)據(jù)持久層的開發(fā)效率。