如何簡(jiǎn)化JDBC代碼 (轉(zhuǎn))
原文:http://blog.csdn.net/kerry365/archive/2005/09/29/491918.aspx
問(wèn)題的提出
在一個(gè)應(yīng)用程序中,處理JDBC的操作是一個(gè)重復(fù)率較高的工作。當(dāng)你在一個(gè)JDBC數(shù)據(jù)源上執(zhí)行SQL查詢時(shí),你通常需要執(zhí)行下面幾個(gè)步驟:
1.生成SQL語(yǔ)句
2.獲得連接
3.獲得一個(gè)PreparedStatement對(duì)象
4.在PreparedStatement對(duì)象中設(shè)定需要送入到數(shù)據(jù)庫(kù)的值
5.執(zhí)行SQL語(yǔ)句
6.處理查詢結(jié)果
除此之外,你還需要處理SQLException異常。如果上面列出的這些步驟分散在程序的各個(gè)部分的話,程序中需要多個(gè)try/catch塊來(lái)處理異常。
如果我們仔細(xì)看看上面列出的步驟,就會(huì)發(fā)現(xiàn)在執(zhí)行不同的SQL語(yǔ)句時(shí),上面這些步驟中涉及到的程序代碼變化不會(huì)很大:我們使用同樣的方法獲得數(shù)據(jù)庫(kù)連接和PreperedStatement對(duì)象;使用setXXX方法來(lái)設(shè)定PreperedStatement對(duì)象中的值;處理SQL查詢結(jié)果的過(guò)程也是基本不變的。在這篇文章中,我們通過(guò)定義三個(gè)JDBC模型,去除了上述六個(gè)步驟中的三個(gè)步驟,這樣使得整個(gè)過(guò)程更加簡(jiǎn)單,而且具有更好的通用性。
查詢模型
我們定義了一個(gè)名叫SQLProcessor的類,在該類中定義了一個(gè)executeQuery()方法來(lái)執(zhí)行SQL語(yǔ)句,我們?cè)趯?shí)現(xiàn)這個(gè)方法的時(shí)候盡量保持代碼的簡(jiǎn)潔性,并且傳遞盡可能少的參數(shù)給該方法。下面是該方法的定義:
|
我們知道在執(zhí)行SQL語(yǔ)句的JDBC過(guò)程中,變化的因素有三個(gè):SQL語(yǔ)句,PreparedStatement對(duì)象和如何解釋和處理查詢結(jié)果。在上面的方法定義中,sql中保存的就是SQL語(yǔ)句;pStmntValues對(duì)象數(shù)組保存的是需要放入preparedStatement對(duì)象中的值;processor參數(shù)是一個(gè)能夠處理查詢結(jié)果的對(duì)象,于是我們把JDBC程序涉及到的對(duì)象分成了三個(gè)部分。下面讓我們來(lái)看一下executeQuery()和與它相關(guān)的一些方法的實(shí)現(xiàn):
|
程序中有兩個(gè)方法需要說(shuō)明:PreparedStatementFactory.buildStatement()和processor.process()。buildStatement()方法把在pStmntValues對(duì)象數(shù)組中的所有對(duì)象送到prepareStatement對(duì)象中的相應(yīng)位置。例如:
|
因?yàn)閟tmnt.setOject(int index, Object value)方法不能接受一個(gè)空對(duì)象作為參數(shù)。為了使程序能夠處理空值,我們使用了自己設(shè)計(jì)的NullSQLType類。當(dāng)一個(gè)NullSQLType對(duì)象被初始化的時(shí)候,將會(huì)保存數(shù)據(jù)庫(kù)表中相應(yīng)列的SQL類型。在上面的例子中我們可以看到,NULLSQLType對(duì)象的屬性中保存了一個(gè)SQL NULL實(shí)際對(duì)應(yīng)的SQL類型。我們用NULLSQLType對(duì)象的getFieldType()方法來(lái)向preparedStatement對(duì)象填入空值。
下面讓我們來(lái)看一看processor.process()方法。Processor類實(shí)現(xiàn)了ResultProcessor接口,該接口是用來(lái)處理SQL查詢結(jié)果的,它只有一個(gè)方法process(),該方法返回了處理SQL查詢結(jié)果后生成的對(duì)象數(shù)組。
|
process()的典型實(shí)現(xiàn)方法是遍歷查詢后返回的ResultSet對(duì)象,將保存在ResultSet對(duì)象中的值轉(zhuǎn)化為相應(yīng)的對(duì)象放入對(duì)象數(shù)組。下面我們通過(guò)一個(gè)例子來(lái)說(shuō)明如何使用這些類和接口。例如當(dāng)我們需要從數(shù)據(jù)庫(kù)的一張用戶信息表中取出用戶信息,表名稱為User:
列名 | 數(shù)據(jù)類型 |
ID | NUMBER |
UserName | VARCHAR2 |
VARCHAR2 |
我們需要在程序中定義一個(gè)類User來(lái)映射上面的表:
|
如果我們使用常規(guī)方法來(lái)讀取User表中的數(shù)據(jù),我們需要一個(gè)方法來(lái)從數(shù)據(jù)庫(kù)表中讀取數(shù)據(jù),然后將數(shù)據(jù)送入U(xiǎn)ser對(duì)象中。而且一旦查詢語(yǔ)句發(fā)生變化,我們需要修改大量的代碼。讓我們看一看采用本文描述的解決方案情況會(huì)如何。
首先構(gòu)造一個(gè)SQL語(yǔ)句。
|
然后創(chuàng)建一個(gè)ResultProcessor接口的實(shí)例類,通過(guò)它我們可以從查詢結(jié)果中獲得一個(gè)User對(duì)象。
|
最后,將執(zhí)行SQL查詢和返回User對(duì)象的指令放入getUser()方法中。
|
這就是我們需要做的全部工作:只需要實(shí)現(xiàn)一個(gè)processor類和一個(gè)getUser()方法。與傳統(tǒng)的JDBC程序相比,在本文描述的模型中,我們不需要處理數(shù)據(jù)庫(kù)連接操作,生成prepareStatement對(duì)象和異常處理部分的代碼。如果需要在同一張表中根據(jù)用戶名查用戶ID,我們只需要在代碼中申明新的查詢語(yǔ)句,然后重用UserResultProcessor類中的大部分代碼。
更新模型
如果SQL語(yǔ)句中涉及到更新,情況又會(huì)怎樣呢?我們可以用類似于設(shè)計(jì)查詢模型的方法來(lái)設(shè)計(jì)更新模型,我們需要向SQLProcessor類中增加一些新的方法。這些方法同executeQuery()和handleQuery()方法有相似之處,只是我們需要改變一下處理ResultSet對(duì)象的代碼,并且把更新的行數(shù)作為方法的返回值。
|
上面的兩個(gè)方法和處理查詢的方法不同之處在于他們?nèi)绾翁幚矸祷刂?。由于更新語(yǔ)句只需要返回被更新了的行數(shù),所以我們不需要處理SQL操作返回的結(jié)果。實(shí)際上有些情況下連被更新了的行數(shù)都不需要返回,我們這樣做的原因是在某些情況下需要確認(rèn)更新操作已經(jīng)完成。
我們?cè)O(shè)計(jì)了UpdateProcessor接口來(lái)處理Update操作返回的更新行數(shù)。
|
例如在程序中需要保證更新操作,更新表中的至少一條記錄。在UpdateProcessor接口的實(shí)現(xiàn)類中就可以加入對(duì)修改行數(shù)的檢測(cè),當(dāng)沒(méi)有記錄被更新時(shí),processor()方法可以拋出自定義的異常;也可以將更新的行數(shù)記錄到日志文件中;或者激發(fā)一個(gè)自定義的更新事件。總而言之,你可以在其中做任何事。
下面是一個(gè)使用更新模型的例子:
首先生成SQL語(yǔ)句
|
實(shí)現(xiàn)UpdateProcessor接口。在Processor()方法中,檢查Update操作是否更新了數(shù)據(jù)。如果沒(méi)有,拋出IllegalStateException異常。
|
最后在updateUser()方法中執(zhí)行Update操作并處理結(jié)果。
|
事務(wù)模型
在數(shù)據(jù)庫(kù)中,事務(wù)和獨(dú)立的SQL語(yǔ)句的區(qū)別在于事務(wù)在生命期內(nèi)使用一個(gè)數(shù)據(jù)庫(kù)連接,并且AutoCommit屬性必須被設(shè)為False。因此我們需要指定事務(wù)何時(shí)開始,何時(shí)結(jié)束,并且在事務(wù)結(jié)束時(shí)提交事務(wù)。我們可以重用SQLProcessor中的大部分代碼來(lái)處理事務(wù)。也許在最開始讀者會(huì)問(wèn)為什么要把執(zhí)行更新和處理更新的工作放在 executeUpdate()和handleUpdate()兩個(gè)函數(shù)中完成--實(shí)際上它們是可以被合并到同一個(gè)函數(shù)中的。這樣做的原因是把處理數(shù)據(jù)庫(kù)連接的代碼和處理SQL操作的代碼分離開來(lái)。對(duì)于需要在多個(gè)SQL操作間共用數(shù)據(jù)庫(kù)連接的事務(wù)模型來(lái)說(shuō),這種方案便于編碼。
在事務(wù)中,我們需要保存事務(wù)的狀態(tài),特別是數(shù)據(jù)庫(kù)連接的狀態(tài)。前面的SQLProcessor中沒(méi)有保存狀態(tài)的屬性,為了保證對(duì)SQLProcessor類的重用,我們?cè)O(shè)計(jì)了一個(gè)包裝類,該類包裝了SQLProcessor類,并且可以維護(hù)事務(wù)在生命周期內(nèi)的狀態(tài)。
|
SQLTransaction中出現(xiàn)了一些新方法,這些方法主要是用來(lái)處理數(shù)據(jù)庫(kù)連接和進(jìn)行事務(wù)管理的。當(dāng)一個(gè)事務(wù)開始時(shí),SQLTransaction對(duì)象獲得一個(gè)新的數(shù)據(jù)庫(kù)連接,并將連接的AutoCommit設(shè)定為False,隨后的所有SQL語(yǔ)句都是用同一個(gè)連接。
只有當(dāng)commitTransaction()被調(diào)用時(shí),事務(wù)才會(huì)被提交。如果執(zhí)行SQL語(yǔ)句的過(guò)程中發(fā)生了異常,程序會(huì)自動(dòng)發(fā)出一個(gè)回滾申請(qǐng),以恢復(fù)程序?qū)?shù)據(jù)庫(kù)所作的改變。對(duì)于開發(fā)人員來(lái)說(shuō),不需要擔(dān)心在出現(xiàn)異常后處理回滾或關(guān)閉連接的工作。下面是一個(gè)使用事務(wù)模型的例子。
|
在例子中我們只使用了更新語(yǔ)句(在大多數(shù)情況下事務(wù)都是由更新操作構(gòu)成的),查詢語(yǔ)句的實(shí)現(xiàn)方法和更新語(yǔ)句類似。
問(wèn)題
在實(shí)際使用上面提到的這些模型時(shí),我遇到了一些問(wèn)題,下面是這些問(wèn)題的小結(jié),希望對(duì)大家有所幫助。
自定義數(shù)據(jù)庫(kù)連接
在事務(wù)處理的時(shí)候,有可能發(fā)生在多個(gè)事務(wù)并存的情況下,它們使用的數(shù)據(jù)庫(kù)連接不同的情況。ConnectionManager需要知道它應(yīng)該從數(shù)據(jù)庫(kù)連接池中取出哪一個(gè)連接。你可以簡(jiǎn)單修改一下模型來(lái)滿足上面的要求。例如在executeQuery()和executeUpdate()方法中,你可以把數(shù)據(jù)庫(kù)連接作為參數(shù),然后將它們傳送給ConnectionManager對(duì)象。請(qǐng)記住所有的連接管理都應(yīng)該放在executeXXX()方法中。另外一種解決方案,也是一種更面向?qū)ο蠡慕鉀Q方案,是將一個(gè)連接工廠作為參數(shù)傳遞給SQLProcessor的構(gòu)造函數(shù)。對(duì)于不同的連接工廠類型,我們需要不同的SQLProcessor對(duì)象。
ResultProcessor類的返回值:對(duì)象數(shù)組還是List?
為什么ResultProcessor接口中的process()方法返回的是對(duì)象數(shù)組呢?怎么不使用List類呢?這是由于在很多實(shí)際的應(yīng)用中,SQL查詢?cè)诖蠖鄶?shù)情況下值返回一行數(shù)據(jù),在這種情況下,使用List對(duì)象會(huì)有些多余了。但是如果你確信SQL查詢將返回多行結(jié)果,你可以使用List對(duì)象。
數(shù)據(jù)庫(kù)操作異常
我們可以用多個(gè)自定義的數(shù)據(jù)庫(kù)操作異常類來(lái)替代運(yùn)行時(shí)發(fā)生的SQLException異常。最好在這些自定義的異常類時(shí)繼承RuntimeException類,這樣可以將這些異常進(jìn)行集中處理。也許你會(huì)認(rèn)為因該將異常處理放在發(fā)生異常的地方。但是我們?cè)O(shè)計(jì)這個(gè)的模型的目的之一是在JDBC應(yīng)用程序開發(fā)中去掉或弱化異常處理的部分,只有使用RuntimeException我們才可能達(dá)到這個(gè)目的。
posted on 2007-10-15 22:20 都市淘沙者 閱讀(165) 評(píng)論(0) 編輯 收藏 所屬分類: Java Basic/Lucene/開源資料