文/武宗品
信息化系統(tǒng)包含信息的采集、處理、分析和統(tǒng)計,在此過程中,需要進行信息的持久化存儲。信息系統(tǒng)當(dāng)前大多數(shù)使用關(guān)系型數(shù)據(jù)庫來存儲信息。
根據(jù)TIOBE 排行榜,Java 依然是排名第一的開發(fā)語言,因此公司使用Java 開發(fā)信息化系統(tǒng)。面向?qū)ο笫荍ava 的一大特點,在使用面向?qū)ο蠓椒ㄟM行系統(tǒng)開發(fā)時,需要用實體對象來表示所處理的信息。因此,就需要不斷的對信息進行關(guān)系型數(shù)據(jù)和實體對象的互轉(zhuǎn),即所謂的對象關(guān)系映射(Object-Relational Mapping,簡稱ORM)。
目前有多種框架幫助開發(fā)者簡化ORM 的開發(fā),例如Hiberante 和iBatis。這些框架有諸多優(yōu)勢,但同時也存在天然缺陷:由于是在程序運行時才會生產(chǎn)數(shù)據(jù)庫操作,導(dǎo)致在開發(fā)時難以調(diào)試,難以進行SQL 優(yōu)化。
針對上述問題,本文提出并實現(xiàn)了一個輕量級的Java 數(shù)據(jù)庫操作組件——DatabaseOperator,該組件為開發(fā)人員提供一個簡單、高效、方便調(diào)試和優(yōu)化的數(shù)據(jù)庫操作方法。由于可以自行控制SQL 語句,并且無需關(guān)注JDBC 的各種狀態(tài)維護,為開發(fā)人員提供了更大的靈活性,減輕了許多冗余代碼。該組件已經(jīng)在公司開發(fā)的多套系統(tǒng)中使用。
數(shù)據(jù)庫操作組件DatabaseOperator 為了保留調(diào)試和SQL 優(yōu)化的特性,選擇采用JDBC來直接操作數(shù)據(jù)庫。同時,為了簡化開發(fā)人員的操作,降低代碼冗余,減少開發(fā)人員對數(shù)據(jù)庫連接的維護工作,組件將采用狀態(tài)模式和觀察者模式這兩個設(shè)計模式作為設(shè)計指導(dǎo)。
Erich Gamma 等人給出了設(shè)計模式的定義,可以這樣去理解它:設(shè)計模式和語言無關(guān),通常是一種針對某類問題或場景,將變化的部分抽離出去,將不變的部分保留下來,最終形成的一個解決問題的套路或思考范式。本小節(jié)將對用到的兩個設(shè)計模式進行簡單介紹。
圖1:標(biāo)準(zhǔn)數(shù)據(jù)庫操作
圖2:數(shù)據(jù)庫操作狀態(tài)
1.1.1 狀態(tài)模式
該模式的定義:狀態(tài)模式允許對象在內(nèi)部狀態(tài)改變時改變它的行為,對象看起來好像修改了它的類。普遍的說,當(dāng)要處理的事情有不同的狀態(tài),并且在不同的狀態(tài)有不同的行為,狀態(tài)與狀態(tài)之間有明確的轉(zhuǎn)換觸發(fā)條件,對于存在這種情況的可以使用狀態(tài)模式來指導(dǎo)設(shè)計,規(guī)范代碼結(jié)構(gòu)。
1.1.2 觀察者模式
該模式的定義:觀察者模式定義了對象之間的一對多依賴,這樣一來,當(dāng)一個對象改變狀態(tài)時,它的所有依賴者都會收到通知并自動更新。普遍的說,當(dāng)要處理的事情有一個明確的第三方,該第三方需要獲取相關(guān)對象的行為,并在全部或者某些行為時第三方有自己的行動,對于存在這種情況的可以使用觀察者模式來指導(dǎo)設(shè)計。
Java 數(shù)據(jù)庫連接,(Java Database Connectivity,簡稱JDBC)是Java 語言中用來規(guī)范客戶端程序如何來訪問數(shù)據(jù)庫的應(yīng)用程序接口,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的方法。
本小節(jié)首先介紹標(biāo)準(zhǔn)的數(shù)據(jù)庫操作方法,然后通過分析,設(shè)計出一套簡潔高效的數(shù)據(jù)庫操作組件并進行實現(xiàn)。
如圖1所示,是一個Java 上的標(biāo)準(zhǔn)數(shù)據(jù)庫操作步驟:①定義變量;②連接數(shù)據(jù)庫;③業(yè)務(wù)操作(本例是一個最簡單的業(yè)務(wù)查詢操作);④處理業(yè)務(wù)異常;⑤關(guān)閉數(shù)據(jù)庫連接。
上節(jié)所介紹的標(biāo)準(zhǔn)數(shù)據(jù)庫操作中,對于開發(fā)人員需要關(guān)注的只有“③業(yè)務(wù)操作”這一部分,可以看到其余都是無需關(guān)注的,如何抽離那些無需關(guān)注的而只保留需要關(guān)注的部分是實現(xiàn)組件的關(guān)鍵。
通過分析,可知在操作數(shù)據(jù)庫的時候,經(jīng)歷了如下幾個步驟:參數(shù)初始化;連接數(shù)據(jù)庫;處理業(yè)務(wù);完成并關(guān)閉。在每一個步驟中,都會有不同的操作,并且步驟之間的轉(zhuǎn)換都是通過固定的操作來完成的。針對這種場景,使用“狀態(tài)模式”來進行設(shè)計:將數(shù)據(jù)庫操作的每一步都看作一個狀態(tài),在獨立的狀態(tài)中,只需關(guān)注當(dāng)前狀態(tài)要做的事情,狀態(tài)與狀態(tài)之間的轉(zhuǎn)換,交給幾個專門的處理方法來完成即可。圖1所示的數(shù)據(jù)庫操作只是基本的業(yè)務(wù)查詢操作,考慮到還有批量操作、存儲過程操作和事務(wù)操作,最終設(shè)計的狀態(tài)機如圖2所示:init(初始狀態(tài));transaction(事務(wù)狀態(tài));sqlDone(業(yè)務(wù)狀態(tài));callable(存儲過程狀態(tài));batch(批量業(yè)務(wù)狀態(tài))。觸發(fā)狀態(tài)轉(zhuǎn)換的操作有:startTransaction,開啟事務(wù);commit,提交事務(wù);setSql,設(shè)定數(shù)據(jù)庫操作語句;setPS,動態(tài)SQL 參數(shù);execute/query,增刪改查;setCallable,設(shè)定存儲過程;setCallableHandler,設(shè)定存儲過程回掉;addSql,添加批量SQL;addBatch,添加批量操作;executeBatch,執(zhí)行批量操作;end,結(jié)束操作。
圖3:實現(xiàn)
通過分析,數(shù)據(jù)庫在操作的過程中,為了讓開發(fā)人員專注于業(yè)務(wù),而無需關(guān)注和維護當(dāng)前數(shù)據(jù)庫連接的建立與關(guān)閉,需要有第三方來監(jiān)控現(xiàn)在的操作狀態(tài),并在操作結(jié)束時關(guān)閉數(shù)據(jù)庫連接、回收對象。針對這種場景,使用“觀察者模式”來指導(dǎo)設(shè)計:當(dāng)開始進行數(shù)據(jù)庫操作的時候,監(jiān)控對象訂閱數(shù)據(jù)庫操作對象的通知,數(shù)據(jù)庫操作結(jié)束時發(fā)出通知,監(jiān)控對象負責(zé)完成關(guān)閉數(shù)據(jù)庫連接和回收對象的工作。
根據(jù)分析與設(shè)計,數(shù)據(jù)庫操作組件的實現(xiàn)如圖3所示。
開發(fā)人員在使用該組件的時候,需要操作圖1所示的業(yè)務(wù)時,實際代碼如圖4所示。由此可見,不僅保留了SQL 的可調(diào)式性,也大大減少了開發(fā)的工作量。
在結(jié)合了狀態(tài)模式和觀察者模式之后,對JDBC 進行了重新的封裝,完成了DatabaseOperator 數(shù)據(jù)庫操作組件的開發(fā)。使用該組件,無需引用大量的框架,能夠快速搭建信息系統(tǒng),簡化代碼工作量,保持SQL 可調(diào)式,確保數(shù)據(jù)庫優(yōu)化的可控性,為公司多個項目的開發(fā)提高了效率,降低了出錯率。同時,在實踐中也發(fā)現(xiàn)該組件還有可以不斷優(yōu)化的地方,例如:與數(shù)據(jù)庫的連接除了自己創(chuàng)建之外,還可以交給數(shù)據(jù)庫連接池來進行管理,從而提高連接效率;數(shù)據(jù)查詢后的結(jié)果,可以使用反射機制將查詢后的結(jié)果直接轉(zhuǎn)變成對象。
圖4:使用組件后的數(shù)據(jù)庫操作