當前位置:首頁 » Java教學 » Java JDBC類庫

Java JDBC類庫

Java JDBC類庫用法實例 DriverManager Connection Statement PreparedStatement CallableStatement ResultSet

JDBC類庫

有了 JDBC,向各種關係數據庫發送 SQL 語句就是一件很容易的事。換言之,有了 JDBC API,就不必為訪問 Sybase 數據庫專門寫一個程序,為訪問 Oracle 數據庫又專門寫一個程序,為訪問 Informix 數據庫又寫另一個程序,等等。您隻需用 JDBC API 寫一個程序就夠了,它可向相應數據庫發送 SQL 語句。而且,使用 Java 編程語言編寫的應用程序,就無須去憂慮要為不同的平台編寫不同的應用程序。將 Java 和 JDBC 結合起來將使程序員隻須寫一遍程序就可讓它在任何平台上運行。

下麵為常用的處理流程:

DriverManager

Connection

Statement

PreparedStatement

CallableStatement

ResultSet

簡單地說,JDBC 可做三件事:

1.         與數據庫建立連接

2.         發送 SQL 語句

3.         處理結果

下列代碼段給出了以上三步的基本示例:

Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

Connection conn = DriverManager.getConnection (

"jdbc:oracle:thin:@eai-sol:1521:eai_db", "csc2", "csc2");

Statement stmt = conn.createStatement();

ResultSet rs = stmt.executeQuery("SELECT CONTACTID FROM CONTACTINFO");

while (rs.next()) {long contactID = rs.getLong("CONTACTID");}

下麵對常用的幾個類和接口做些簡單的說明。

DriverManager:

DriverManager類是 JDBC 的管理層,作用於用戶和驅動程序之間。它跟蹤可用的驅動程序,並在數據庫和相應驅動程序之間建立連接。另外,DriverManager 類也處理諸如驅動程序登錄時間限製及登錄和跟蹤消息的顯示等事務。對於簡單的應用程序,一般程序員需要在此類中直接使用的唯一方法是 DriverManager.getConnection。正如名稱所示,該方法將建立與數據庫的連接。

Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();

Connection conn = DriverManager.getConnection (

"jdbc:oracle:thin:@eai-sol:1521:eai_db", "csc2", "csc2");

其中第一句話的作用是在當前的環境中load一個DB Driver, 有人可能覺得奇怪, 這句話執行完之後, 後麵怎麼知道去用這個Driver呢? 其實DriverManager可以從load的classes裡麵找到注冊過的driver,然後使用它所找到的第一個可以成功連接到給定 URL 的驅動程序。 第二句話的三個參數分彆是URL, User, Password。Driver不一樣, URL可能也不一樣。


Statement:

Statement 對象用於將 SQL 語句發送到數據庫中。實際上有三種 Statement 對象,它們都為在給定連接上執行 SQL 語句的包容器:Statement、PreparedStatement(它從 Statement 承而來)和 CallableStatement(它從 PreparedStatement 繼承而來)。它們都專用於發送定類型的 SQL 語句: Statement 對象用於執行不帶參數的簡單 SQL 語句;PreparedStatement 對象用於執行帶或不帶 IN 參數的預編譯 SQL 語句;CallableStatement 對象用於執行對數據庫已存儲過程的調用。

Statement 接口提供了執行語句和獲取結果的基本方法。PreparedStatement 接口添加了處理 IN 參數的方法;而 CallableStatement 添加了處理 OUT 參數的方法。

Ø         創建 Statement 對象
建立了到特定數據庫的連接之後,就可用該連接發送 SQL 語句。Statement 對象用 Connection 的方法 createStatement 創建,如下列代碼段中所示:
Statement stmt = conn.createStatement();

為了執行 Statement 對象,被發送到數據庫的 SQL 語句將被作為參數提供給 Statement 的方法: 
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM table1");

Ø         使用 Statement 對象執行語句

Statement 接口提供了三種執行 SQL 語句的方法:executeQuery、executeUpdate 和execute。使用哪一個方法由 SQL 語句所產生的內容決定。
方法 executeQuery 用於產生單個結果集的語句,例如 SELECT 語句。

方法 executeUpdate 用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQL DDL(數據定義語言)語句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 語句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一個整數,指示受影響的行數(即更新計數)。對於 CREATE TABLE 或 DROP TABLE 等不操作行的語句,executeUpdate 的返回值總為零。

方法 execute 用於執行返回多個結果集、多個更新計數或二者組合的語句。

Ø         語句完成

當連接處於自動提交模式時,其中所執行的語句在完成時將自動提交或還原。語句在已執行且所有結果返回時,即認為已完成。對於返回一個結果集的 executeQuery 方法,在檢索完 ResultSet 對象的所有行時該語句完成。對於方法 executeUpdate,當它執行時語句即完成。但在少數調用方法 execute 的情況中,在檢索所有結果集或它生成的更新計數之後語句才完成。

Ø         關閉 Statement 對象

Statement 對象將由 Java 垃圾收集程序自動關閉。而作為一種好的編程風格,應在不需要 Statement 對象時顯式地關閉它們。這將立即釋放 DBMS 資源,有助於避免潛在的內存問題。 關閉Statement用 stmt.close() 方法。


ResultSet:

ResultSet 包含符合 SQL 語句中條件的所有行,並且它通過一套 get 方法(這些 get 方法可以訪問當前行中的不同列)提供了對這些行中數據的訪問。ResultSet.next 方法用於移動到 ResultSet 中的下一行,使下一行成為當前行。

結果集一般是一個表,其中有查詢所返回的列標題及相應的值。例如,如果查詢為 SELECT a, b, c FROM Table1,則結果集將具有如下形式:

a        b         c

-------- --------- --------

12345    Cupertino CA

83472    Redmond   WA

83492    Boston    MA
下麵的代碼段是執行 SQL 語句的示例。該 SQL 語句將返回行集合,其中列 1 為 int,列 2 為 String,而列 3 則為日期型:

Statement stmt = conn.createStatement();

ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");

while (rs.next()) {

int i = rs.getInt("a");

String s = rs.getString("b");

Timestamp t = rs.getTimestamp("c");

}

Ø         行和光標

ResultSet 維護指向其當前數據行的光標。每調用一次 next 方法,光標向下移動一行。最初它位於第一行之前,因此第一次調用 next 將把光標置於第一行上,使它成為當前行。隨著每次調用 next 導致光標向下移動一行,按照從上至下的次序獲取 ResultSet 行。在 ResultSet 對象或其父輩 Statement 對象關閉之前,光標一直保持有效。

Ø         列

方法 getXXX 提供了獲取當前行中某列值的途徑。在每一行內,可按任何次序獲取列值。但為了保證可移植性,應該從左至右獲取列值,並且一次性地讀取列值。列名或列號可用於標識要從中獲取數據的列。例如,如果 ResultSet 對象 rs 的第二列名為“title”,並將值存儲為字符串,則下列任一代碼將獲取存儲在該列中的值:

String s = rs.getString("title");

String s = rs.getString(2);

注意列是從左至右編號的,並且從列 1 開始。同時,用作 getXXX 方法的輸入的列名不區分大小寫。 為了代碼的可維護性與可讀性, 應該禁止用index的方法來取值, 要用讀列名的方法, 如上麵的第一行取值方法。

Ø         數據類型和轉換

對於 getXXX 方法,JDBC 驅動程序試圖將基本數據轉換成指定 Java 類型,然後返回適合的 Java 值。例如,如果 getXXX 方法為 getString,而基本數據庫中數據類型為 VARCHAR,則 JDBC 驅動程序將把 VARCHAR 轉換成 Java String。getString 的返回值將為 Java String 對象。

Ø         NULL 結果值

要確定給定結果值是否是 JDBC NULL,必須先讀取該列,然後使用 ResultSet.wasNull 方法檢查該次讀取是否返回 JDBC NULL。

PreparedStatement:

該 PreparedStatement 接口繼承 Statement,並與之在兩方麵有所不同:

PreparedStatement 實例包含已編譯的 SQL 語句。這就是使語句“準備好”。 包含於 PreparedStatement 對象中的 SQL 語句可具有一個或多個 IN 參數。IN 參數的值在 SQL 語句創建時未被指定。相反的,該語句為每個 IN 參數保留一個問號(“?”)作為占位符。每個問號的值必須在該語句執行之前,通過適當的 setXXX 方法來提供。

由於 PreparedStatement 對象已預編譯過,所以其執行速度要快於 Statement 對象。因此,多次執行的 SQL 語句經常創建為 PreparedStatement 對象,以提高效率。

作為 Statement 的子類,PreparedStatement 繼承了 Statement 的所有功能。另外它還添加了一整套方法,用於設置發送給數據庫以取代 IN 參數占位符的值。同時,三種方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要參數。

Ø         創建 PreparedStatement 對象

以下的代碼段(其中 conn 是 Connection 對象)創建包含帶兩個 IN 參數占位符的 SQL 語句的 PreparedStatement 對象:

PreparedStatement pstmt=conn.prepareStatement("UPDATE table1 SET a = ? WHERE b = ?");

pstmt 對象包含語句 " UPDATE table1 SET a = ? WHERE b = ?",它已發送給 DBMS,並為執行作好了準備。

Ø         傳遞 IN 參數

在執行 PreparedStatement 對象之前,必須設置每個 ? 參數的值。這可通過調用 setXXX 方法來完成,其中 XXX 是與該參數相應的類型。例如,如果參數具有 Java 類型 long,則使用的方法就是 setLong。setXXX 方法的第一個參數是要設置的參數的序數位置,第二個參數是設置給該參數的值。例如,以下代碼將第一個參數設為 123456789,第二個參數設為 100000000:

pstmt.setLong(1, 123456789);

pstmt.setLong(2, 100000000);

CallableStatement:

CallableStatement 對象為所有的 DBMS 提供了一種以標準形式調用已儲存過程(也就是SP)的方法。已儲存過程儲存在數據庫中。對已儲存過程的調用是 CallableStatement 對象所含的內容。有兩種形式:一種形式帶結果參數,另一種形式不帶結果參數。結果參數是一種輸出 (OUT) 參數,是已儲存過程的返回值。兩種形式都可帶有數量可變的輸入(IN 參數)、輸出(OUT 參數)或輸入和輸出(INOUT 參數)的參數。問號將用作參數的占位符。

在 JDBC 中調用已儲存過程的語法如下所示。注意,方括號表示其間的內容是可選項;方括號本身並不是語法的組成部份。

{call 過程名[(?, ?, ...)]}

返回結果參數的過程的語法為:

{? = call 過程名[(?, ?, ...)]}

不帶參數的已儲存過程的語法類似:

{call 過程名}

通常,創建 CallableStatement 對象的人應當知道所用的 DBMS 是支持已儲存過程的,並且知道這些過程都是些什麼。然而,如果需要檢查,多種 DatabaseMetaData 方法都可以提供這樣的信息。例如,如果 DBMS 支持已儲存過程的調用,則 supportsStoredProcedures 方法將返回 true,而 getProcedures 方法將返回對已儲存過程的描述。

CallableStatement 繼承 Statement 的方法(它們用於處理一般的 SQL 語句),還繼承了 PreparedStatement 的方法(它們用於處理 IN 參數)。CallableStatement 中定義的所有方法都用於處理 OUT 參數或 INOUT 參數的輸出部分:注冊 OUT 參數的 JDBC 類型(一般 SQL 類型)、從這些參數中檢索結果,或者檢查所返回的值是否為 JDBC NULL。

Ø         創建 CallableStatement 對象

CallableStatement 對象是用 Connection 方法 prepareCall 創建的。下例創建 CallableStatement 的實例,其中含有對已儲存過程 Csc_ GetCustomId調用。該過程有兩個變量,但不含結果參數:

CallableStatement cstmt = con.prepareCall("{call CSC_GetCustomId (?, ?, ?)}");

其中 ? 占位符為 IN、 OUT 還是 INOUT 參數,取決於已儲存過程 Csc_ GetCustomId。

Ø         IN 和 OUT 參數

將IN 參數傳給 CallableStatement 對象是通過 setXXX 方法完成的。該方法繼承自 PreparedStatement。所傳入參數的類型決定了所用的 setXXX 方法(例如,用 setFloat 來傳入 float 值等)。

如果已儲存過程返回 OUT 參數,則在執行 CallableStatement 對象以前必須先注冊每個 OUT 參數的 JDBC 類型(這是必需的,因為某些 DBMS 要求 JDBC 類型)。注冊 JDBC 類型是用 registerOutParameter 方法來完成的。語句執行完後,CallableStatement 的 getXXX 方法將取回參數值。正確的 getXXX 方法是為各參數所注冊的 JDBC 類型所對應的 Java 類型也就是說, registerOutParameter 使用的是 JDBC 類型(因此它與數據庫返回的 JDBC 類型匹配),而 getXXX 將之轉換為 Java 類型。下麵給出CSC中的一個例子:

String sqlSp = "{call CSC_GetCustomId(?, ?, ?)}";

cstmt = conn.prepareCall(sqlSp.toString());

cstmt.registerOutParameter(1, Types.NUMERIC);

cstmt.registerOutParameter(2, Types.NUMERIC);

cstmt.registerOutParameter(3, Types.VARCHAR);

cstmt.execute();

long customerID = cstmt.getLong(1);

long lRet = cstmt.getLong(2);

String sErr = cstmt.getString(3);

Ø         INOUT 參數

既支持輸入又接受輸出的參數(INOUT 參數)除了調用 registerOutParameter 方法外,還要求調用適當的 setXXX 方法(該方法是從 PreparedStatement 繼承來的)。setXXX 方法將參數值設置為輸入參數,而 registerOutParameter 方法將它的 JDBC 類型注冊為輸出參數。setXXX 方法提供一個 Java 值,而驅動程序先把這個值轉換為 JDBC 值,然後將它送到數據庫中。這種 IN 值的 JDBC 類型和提供給 registerOutParameter 方法的 JDBC 類型應該相同。然後,要檢索輸出值,就要用對應的 getXXX 方法。例如,Java 類型為 byte 的參數應該使用方法 setByte 來賦輸入值。應該給 registerOutParameter 提供類型為 TINYINT 的 JDBC 類型,同時應使用 getByte 來檢索輸出值。下例假設有一個已儲存過程 reviseTotal,其唯一參數是 INOUT 參數。方法 setByte 把此參數設為 25,驅動程序將把它作為 JDBC TINYINT 類型送到數據庫中。接著,registerOutParameter 將該參數注冊為 JDBC TINYINT。執行完該已儲存過程後,將返回一個新的 JDBC TINYINT 值。方法 getByte 將把這個新值作為 Java byte 類型檢索。

CallableStatement cstmt = con.prepareCall("{call reviseTotal(?)}");

cstmt.setByte(1, 25);

cstmt.registerOutParameter(1, java.sql.Types.TINYINT);

cstmt.executeUpdate();

byte x = cstmt.getByte(1);