1. 程式人生 > >JDBC基礎教程之CallableStatement

JDBC基礎教程之CallableStatement

概述 

   CallableStatement 物件為所有的 DBMS 提供了一種以標準形式呼叫已儲存過程的方法。已儲存過程儲存在資料庫中。對已儲存過程的呼叫是 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。

 1、建立 CallableStatement 物件

  CallableStatement 物件是用 Connection 方法 prepareCall 建立的。下例建立 CallableStatement 的例項,其中含有對已儲存過程 getTestData 呼叫。該過程有兩個變數,但不含結果引數:

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

  其中?佔位符為IN、OUT還是INOUT引數,取決於已儲存過程getTestData。

  2、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 型別。

  作為示例,下述程式碼先註冊 OUT 引數,執行由 cstmt 所呼叫的已儲存過程,然後檢索在 OUT 引數中返回的值。方法 getByte 從第一個 OUT 引數中取出一個 Java 位元組,而 getBigDecimal 從第二個 OUT 引數中取出一個 BigDecimal 物件(小數點後面帶三位數):

CallableStatement cstmt = con.prepareCall("{call getTestData(?, ?)}");
cstmt.registerOutParameter(
1, java.sql.Types.TINYINT);
cstmt.registerOutParameter(
2, java.sql.Types.DECIMAL, 3);
cstmt.executeQuery();
byte x = cstmt.getByte(1);
java.math.BigDecimal n 
= cstmt.getBigDecimal(23);

CallableStatement 與 ResultSet 不同,它不提供用增量方式檢索大 OUT 值的特殊機制。

  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(
125);
cstmt.registerOutParameter(
1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);

  4、先檢索結果,再檢索 OUT 引數

  由於某些 DBMS 的限制,為了實現最大的可移植性,建議先檢索由執行CallableStatement 物件所產生的結果,然後再用 CallableStatement.getXXX 方法來檢索 OUT 引數。如果 CallableStatement 物件返回多個 ResultSet 物件(通過呼叫 execute 方法),在檢索 OUT 引數前應先檢索所有的結果。這種情況下,為確保對所有的結果都進行了訪問,必須對 Statement 方法 getResultSet、getUpdateCount 和getMoreResults 進行呼叫,直到不再有結果為止。

  檢索完所有的結果後,就可用 CallableStatement.getXXX 方法來檢索 OUT 引數中的值。

  5、檢索作為OUT引數的NULL值

  返回到 OUT 引數中的值可能會是JDBC NULL。當出現這種情形時,將對 JDBC NULL 值進行轉換以使 getXXX 方法所返回的值為 null、0 或 false,這取決於getXXX 方法型別。對於 ResultSet 物件,要知道0或false是否源於JDBCNULL的唯一方法,是用方法wasNull進行檢測。如果 getXXX 方法讀取的最後一個值是 JDBC NULL,則該方法返回 true,否則返回 flase。