作者: Builder.comMonday, April 26 2004 4:43 PM
JDBC碼是Java譯碼的一個(gè)部分,它給已寫的編碼帶來了數(shù)量驚人的重復(fù)。另外,JDBC碼幾乎會(huì)經(jīng)常性的帶來一些低級(jí)錯(cuò)誤。寫出好的JDBC編碼并不難,但是很痛苦。
DbUtils組件是一個(gè)精密而簡單的組件,它并不做什么復(fù)雜的事而僅僅只是使很多的JDBC任務(wù)對(duì)開發(fā)者來說變得稍容易一點(diǎn)。盡管這時(shí)候很多持久框架和包都可以用來使數(shù)據(jù)持久變得更容易,然而JDBC仍然是大多數(shù)Java和Java2企業(yè)版(J2EE)開發(fā)者賴以生存的工具。因此,任何能讓使用JDBC工作更容易的東西都是好消息。
DbUtils可以免費(fèi)下載
,它不依賴于任何其它的通用組件而只是依賴下面這些:
- Java Development Kit (JDK) 1.2 (or later)
- JDBC 2.0 (or later)
DbUtils文檔并不是最好的,但是足以使你的工作正常進(jìn)行。在下一節(jié),你會(huì)看到DbUtils中最有用的類以及一些關(guān)于它們的用法的例子。你應(yīng)該能夠很容易地使用這些編碼和例子,然后能夠馬上在你自己的項(xiàng)目中開始使用DbUtils。我將會(huì)集中精力于兩個(gè)類(org.apache.commons.dbutils.DbUtils 和org.apache.commons.dbutils.QueryRunner)和一個(gè)接口(org.apache.commons.dbutils.ResultSethandler).在我給你們一些關(guān)于它們的用法的例子之前,讓我們深入DbUtils里面來仔細(xì)看看它給我們提供了些什么。
DbUtils
DbUtils是一個(gè)為做一些諸如關(guān)閉連接、裝載JDBC驅(qū)動(dòng)程序之類的常規(guī)工作提供有用方法的類,它里面所有的方法都是靜態(tài)的。
這個(gè)類里的重要方法有:
- close:DbUtils類提供了三個(gè)重載的關(guān)閉方法。這些方法檢查所提供的參數(shù)是不是NULL,如果不是的話,它們就關(guān)閉連接、聲明和結(jié)果集(ResultSet)。
- CloseQuietly: CloseQuietly這一方法不僅能在連接、聲明或者結(jié)果集(ResultSet)為NULL情況下避免關(guān)閉,還能隱藏一些在程序中拋出的SQLEeception。如果你不想捕捉這些異常的話,這對(duì)你是非常有用的。在重載CloseQuietly方法時(shí),特別有用的一個(gè)方法是closeQuietly(Connection conn,Statement stmt,ResultSet rs),這是因?yàn)樵诖蠖鄶?shù)情況下,連接、聲明和結(jié)果集(ResultSet)是你要用的三樣?xùn)|西,而且在最后的塊你必須關(guān)閉它們。使用這一方法,你最后的塊就可以只需要調(diào)用這一方法即可。
- CommitAndCloseQuietly(Connection conn):這一方法用來提交連接,然后關(guān)閉連接,并且在關(guān)閉連接時(shí)不向上拋出在關(guān)閉時(shí)發(fā)生的一些SQL異常。
- LoadDriver(String driveClassName): 這一方法裝載并注冊(cè)JDBC驅(qū)動(dòng)程序,如果成功就返回TRUE。使用這種方法,你不需要去捕捉這個(gè)異常ClassNotFoundException。使用loadDrive方法,編碼就變得更容易理解,你也就得到了一個(gè)很好的Boolean返回值,這個(gè)返回值會(huì)告訴你驅(qū)動(dòng)類是不是已經(jīng)加載成功了。
ResultSetHandler
正如它的名字所提示的,這一接口執(zhí)行處理一個(gè)jaca.sql.ResultSet,將數(shù)據(jù)轉(zhuǎn)變并處理為任何一種形式,這樣有益于其應(yīng)用而且使用起來更容易。這一組件提供了ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, MapHandler, MapListHandler, and ScalarHandler等執(zhí)行程序。
ResultSetHandler接口提供了一個(gè)單獨(dú)的方法:Object handle (java.sql.ResultSet .rs)。因此任何ResultSetHandler 的執(zhí)行需要一個(gè)結(jié)果集(ResultSet)作為參數(shù)傳入,然后才能處理這個(gè)結(jié)果集,再返回一個(gè)對(duì)象。因?yàn)榉祷仡愋褪莏ava.lang.Object,所以除了不能返回一個(gè)原始的Java類型之外,其它的返回類型并沒有什么限制。如果你發(fā)現(xiàn)這七個(gè)執(zhí)行程序中沒有任何一個(gè)提供了你想要的服務(wù),你可以自己寫執(zhí)行程序并使用它。
QreryRunner
這個(gè)類使執(zhí)行SQL查詢簡單化了,它與ResultSetHandler串聯(lián)在一起有效地履行著一些平常的任務(wù),它能夠大大減少你所要寫的編碼。QueryRunner類提供了兩個(gè)構(gòu)造器:其中一個(gè)是一個(gè)空構(gòu)造器,另一個(gè)則拿一個(gè) javax.sql.DataSource 來作為參數(shù)。因此,在你不用為一個(gè)方法提供一個(gè)數(shù)據(jù)庫連接來作為參數(shù)的情況下,提供給構(gòu)造器的數(shù)據(jù)源(DataSource) 被用來獲得一個(gè)新的連接并將繼續(xù)進(jìn)行下去。
這一類中的重要方法包括以下這些:
- query(Connection conn, String sql, Object[] params, ResultSetHandler rsh):這一方法執(zhí)行一個(gè)選擇查詢,在這個(gè)查詢中,對(duì)象陣列的值被用來作為查詢的置換參數(shù)。這一方法內(nèi)在地處理PreparedStatement 和ResultSet 的創(chuàng)建和關(guān)閉。ResultSetHandler對(duì)把從 ResultSet得來的數(shù)據(jù)轉(zhuǎn)變成一個(gè)更容易的或是應(yīng)用程序特定的格式來使用。
- query(String sql, Object[] params, ResultSetHandler rsh):這幾乎與第一種方法一樣;唯一的不同在于它不將數(shù)據(jù)庫連接提供給方法,并且它是從提供給構(gòu)造器的數(shù)據(jù)源(DataSource) 或使用的setDAtaSource 方法中重新獲得的。
- query(Connection conn, String sql, ResultSetHandler rsh):這執(zhí)行一個(gè)不要參數(shù)的選擇查詢。
- update(Connection conn, String sql, Object[] params):這一方法被用來執(zhí)行一個(gè)插入、更新或刪除操作。對(duì)象陣列為聲明保存著置換參數(shù)。
現(xiàn)在讓我們來看一個(gè)例子,在這里你可以從一個(gè)數(shù)據(jù)庫中獲得一些數(shù)據(jù)。比如說,我正在使用MySQL 數(shù)據(jù)庫.你還需要下載MYSQL JDBC驅(qū)動(dòng)程序。我正在使用的MySQL數(shù)據(jù)庫在本地主機(jī),端口號(hào)為3306上運(yùn)行。這個(gè)數(shù)據(jù)庫地名字叫做test。你將要用到的Student表的結(jié)構(gòu)如下:
Columns Type
------- ----
StudId int
Name varchar
在列表A中,你將會(huì)從Student表中得到一些信息,而且你可以按照你自己的額外需要修改這些信息。盡管你在使用JDBC,但要注意你幾乎沒寫JDBC編碼。(你可能要改變?cè)诶又兴?guī)定的用戶名和密碼,這是以你的具體的數(shù)據(jù)庫配置為基礎(chǔ)的。)
這個(gè)編碼遵從以下步驟:
1.加載JDBC驅(qū)動(dòng)程序類,并用DriverManager來得到一個(gè)數(shù)據(jù)庫連接。
2.例示 QueryRunner 類。
3.使用連接、SQL查詢、參數(shù)和ResultSetHandler來作為輸入的查詢方法。你使用一個(gè)類org.apache.commons.dbutils.handlers.MapListHandler,一個(gè)類 MapListHandler來獲得一個(gè)結(jié)果集(ResultSet)并返回一個(gè)jaca.util.Map的實(shí)例java.util.List。因此結(jié)果集(ResultSet) 的每一行都變成了一個(gè)java.util.Map,所有這些java.util.Map的實(shí)例綁在一起放在一個(gè)java.util.List 中。
4. 反復(fù)得到列表(List)的值就是通過在列表(List)中獲得每一個(gè)Map的值。
5.用QueryRunner 來執(zhí)行一個(gè)沒有參數(shù)的方法。在這里你要用BeanListHandler ,它是一個(gè)非常有用的ResultSetHandler ,因?yàn)槟憧梢园裄esultSet 轉(zhuǎn)變成一個(gè)指定的Bean的列表中。這時(shí)你可以指定一個(gè)Bean類到Bean StudentBean中,如同在列表B中所顯示的那樣。
6. 你通過反復(fù)從列表(List)中得到多個(gè)bean,然后就可從每一個(gè)StudentBean實(shí)例中獲取值。
注釋:在列表B中,StudentBean 類中的StudId 必須是int,這是因?yàn)楸鞸tudent的StudId列的類型是int。堅(jiān)持這個(gè)類型的匹配是我們需要遵從的唯一規(guī)則。
因?yàn)樵谶@種情況下,StudentBean 類的屬性和表Student 的字段是完好的對(duì)映著的,只要將StuentBean 類作為一個(gè)參數(shù)就是一個(gè)技巧。字段值用和字段名一樣的名字插入到類的屬性中。然而,如果你想要更多地控制bean的創(chuàng)建,則類BeanListHandler提供了第二個(gè)構(gòu)造器:BeanListHandler(java.lang.Class type, RowProcessor convert). 接口Rowprocessor的執(zhí)行把結(jié)果集(ResultSet)的各行轉(zhuǎn)化成一個(gè)對(duì)象組。在 StudentBean這一案例中,RowProcessor中的BasicRowProcessor 的執(zhí)行被利用上了,它能夠執(zhí)行這項(xiàng)任務(wù)。然而,你可以寫一個(gè)新的執(zhí)行并把它提供給BeanListHandler的構(gòu)造器。
當(dāng)然,執(zhí)行這一編碼的輸出取決于你從表Student中獲得哪些數(shù)據(jù)。對(duì)我來說,我得到了以下這些輸出:
***Using MapListHandler***
Id >>1
Name >>One
Id >>2
Name >>Two
***Using BeanListHandler***
Id >>1
Name >>One
Id >>2
Name >>Two
Id >>3
Name >>Three
除了到目前為止你已經(jīng)看了的類以外,另外一些你需要研究的類是:
- org.apache.commons.dbutils.QueryLoader:QueryLoader是一個(gè)從一個(gè)文件加載查詢到一個(gè)Map的簡單的類。然后,當(dāng)需要的時(shí)候,你從 Map 中選擇一些查詢。在沒有專門去接觸代碼的情況下,一個(gè)文件中的Having查詢也可以改變得盡可能的簡單。
- org.apache.commons.dbutils.wrappers.SqlNullCheckedResultSet:這個(gè)類對(duì)使用一個(gè)系統(tǒng)方法來解決NULL值問題是很有用的。用一個(gè) SqINullCheckedResultSet 的實(shí)例來限制一個(gè)常規(guī)的結(jié)果集(ResultSet) ,然后詳細(xì)地說明在遇NULL值的情況下應(yīng)該做些什么。
- org.apache.commons.dbutils.wrappers.StringTrimmedResultSet:用類StringTrimmedResultSet 來約束一個(gè)結(jié)果集,這樣一來,你就可以修整所有g(shù)etString()和getObject()方法返回的字符串。
DbUtils 組件很好也很小巧,很值得在所有用到JDBC的項(xiàng)目中去使用。
DBUtils的JUnit例子:
//使用dbutils1.0版本
import java.util.*;
import java.util.logging.*;
import java.sql.*;
import org.apache.commons.dbutils.*;
import org.apache.commons.dbutils.handlers.*;
public class TestDBUnits {
public static void main(String[]args) throws Exception {
TestDBUnits test = new TestDBUnits();
for(int i = 0 ; i < 1 ; i++) {
test.testQuery1();
test.testQuery2();
test.testUpdate();
}
}
public void testQuery1(){
try {
QueryRunner qr = new QueryRunner() ;
ResultSetHandler rsh = new ArrayListHandler();
String strsql = "select * from test1";
ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);
//System.out.print("");
} catch(Exception ex) {
ex.printStackTrace(System.out);
}
}
public void testQuery2(){
try {
QueryRunner qr = new QueryRunner() ;
ResultSetHandler rsh = new MapListHandler();
String strsql = "select * from test1";
ArrayList result = (ArrayList)qr.query(getConnection() ,strsql ,rsh);
for(int i = 0 ; i < result.size() ; i++) {
Map map = (Map)result.get(i);
//System.out.println(map);
}
//System.out.print("");
} catch(Exception ex) {
ex.printStackTrace(System.out);
}
}
public void testUpdate(){
try {
QueryRunner qr = new QueryRunner() ;
ResultSetHandler rsh = new ArrayListHandler();
String strsql = "insert test1(page ,writable ,content)values('ttt','ttt','faskldfjklasdjklfjasdklj')";
qr.update(getConnection() ,strsql);
//System.out.print("");
} catch(Exception ex) {
ex.printStackTrace(System.out);
}
}
private Connection getConnection() throws InstantiationException,
IllegalAccessException, ClassNotFoundException, SQLException {
String strDriver = "org.gjt.mm.mysql.Driver";
String strUrl = "jdbc:mysql://localhost:3306/test";
String strUser = "root";
String strPass = "";
Class.forName(strDriver).newInstance();
return DriverManager.getConnection(strUrl, strUser, strPass);
}
}