1、簡述
CallableStatement 是用於執行SQL存儲過程的。JDBC API提供了存儲過程SQL轉義語法,該語法允許所有RDBMS以標準方式調用存儲過程。
Connection 接口提供了 prepareCall(String SQL)方法來創建 CallableStatement 對象,以調用數據庫存儲過程。 CallableStatement對象提供了用於設置其IN和OUT參數的方法,以及用於執行對存儲過程的調用的方法。
2、類圖
下面的類圖顯示了 CallableStatement 接口的繼承關係:
CallableStatement 使用從 PreparedStatement繼承的set方法設置IN參數值。 在執行存儲過程之前,必須先註冊所有OUT參數的類型。 它們的值在執行後通過此處提供的get方法檢索。
一個CallableStatement可以返回一個ResultSet對象或多個ResultSet對象。 使用從Statement繼承的操作來處理多個ResultSet對象。
3、示例
3.1、單個結果集
返回單個結果集的存儲過程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_single_select` ()
BEGIN
SELECT * FROM users;
END$$
DELIMITER ;
以下程序演示瞭如何調用 proc_single_select() 存儲過程並生成單個結果集。
//數據庫連接地址
private final String jdbcUrl = "jdbc:mysql://localhost:3306/lkf_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
//用戶名
private final String username = "root";
//密碼
private final String password = "root";
//調用返回單個結果集存儲過程語句
private final String proc_single_select_sql = "call proc_single_select()";
//調用返回多個結果集存儲過程語句
private final String proc_multi_select_sql = "call proc_multi_select()";
public static void main(String[] args) {
CallableStatementDemo callableStatementDemo = new CallableStatementDemo();
//返回單個結果集
callableStatementDemo.singleResultSet();
}
/**
* 返回單個結果集
*/
public void singleResultSet() {
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
CallableStatement stmt = conn.prepareCall(proc_single_select_sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
System.out.println("ID = " + rs.getInt(1) + ", NAME = " + rs.getString(2) + ", Email = " +
rs.getString(3) + ", Country = " + rs.getString(4) + ", Password = " + rs.getString(5));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
結果:
ID = 1, NAME = 張三, Email = [email protected], Country = china, Password = 123456
ID = 2, NAME = 李四, Email = [email protected], Country = china, Password = 123456
3.2、多個結果集
返回多個結果集的存儲過程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_multi_select` ()
BEGIN
#查詢 id 爲 1,按名字去重後的名字
SELECT DISTINCT NAME FROM users WHERE id = 1;
#按郵箱去重,並獲取去重後的所有郵箱
SELECT DISTINCT email FROM users;
#根據id統計用戶數量
SELECT COUNT(id) AS users_count FROM users;
END$$
DELIMITER ;
以下程序演示瞭如何調用 proc_multi_select() 存儲過程並生成多個結果集。
//數據庫連接地址
private final String jdbcUrl = "jdbc:mysql://localhost:3306/lkf_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
//用戶名
private final String username = "root";
//密碼
private final String password = "root";
//調用返回單個結果集存儲過程語句
private final String proc_single_select_sql = "call proc_single_select()";
//調用返回多個結果集存儲過程語句
private final String proc_multi_select_sql = "call proc_multi_select()";
public static void main(String[] args) {
CallableStatementDemo callableStatementDemo = new CallableStatementDemo();
//返回單個結果集
// callableStatementDemo.singleResultSet();
//返回多個結果集
callableStatementDemo.multipleResultSet();
}
/**
* 返回多個結果集
*/
public void multipleResultSet() {
try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
CallableStatement stmt = conn.prepareCall(proc_multi_select_sql);) {
//執行存儲過程
boolean hasRs = stmt.execute();
System.out.println();
// 解析名字的結果集
if (hasRs) {
try (ResultSet rs = stmt.getResultSet()) {
while (rs.next()) {
System.out.println("NAME = " + rs.getString(1));
}
}
}
// 解析郵箱的結果集
if (stmt.getMoreResults()) {
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) {
System.out.println("Email = " + rs.getString(1));
}
}
}
// 獲取統計的用戶數量
if (stmt.getMoreResults()) {
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) {
System.out.println("Users count = " + rs.getInt(1));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
結果:
NAME = 張三
Email = [email protected]
Users count = 2
3.3、帶參數的存儲過程
存在三種類型的參數:IN,OUT和INOUT。
PreparedStatement對象只使用IN參數。 CallableStatement對象可以使用上面三種類型參數。
參數 | 描述 |
---|---|
IN | 表示調用者傳入值(傳入值可以是字面量或變量)。創建SQL語句時其參數值是未知的。 使用setXXX() 方法將值綁定到IN 參數。 |
OUT | 表示過程向調用者傳出值(可以返回多個值)(傳出值只能是變量)。可以使用getXXX() 方法從OUT參數中檢索值。 |
INOUT | 既表示調用者向過程傳入值,又表示過程向調用者傳出值(值只能是變量)。使用setXXX() 方法綁定變量並使用getXXX() 方法檢索值。 |
使用 IN 參數,只需遵循適用 於PreparedStatement 對象的相同規則,與綁定的Java數據類型相對應的setXXX()方法。
使用 OUT 和 INOUT 參數時,必須使用 CallableStatement 對象的方法 registerOutParameter()。
registerOutParameter() 方法將JDBC數據類型綁定到存儲過程預期返回的數據類型。
當調用存儲過程後,可以使用適當的 getXXX() 方法從OUT參數中檢索該值。 此方法將檢索到的SQL類型的值轉換爲Java 數據類型。
3.3.1、帶有輸入參數(IN)的存儲過程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_getUserByName` (IN p_name VARCHAR(30))
BEGIN
SELECT * FROM users WHERE NAME=p_name;
END$$
DELIMITER ;
以下示例將調用存儲過程 proc_getUserByName 並傳入參數,獲取對應的記錄信息。
private final String proc_in_select_sql = "call proc_getUserByName(?)";
/**
* 帶有輸入參數(IN)的存儲過程
*/
public void procInParam() throws SQLException {
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try {
//獲取連接對象
conn = DriverManager.getConnection(jdbcUrl, username, password);
//預編譯
stmt = conn.prepareCall(proc_in_select_sql);
//傳遞輸入參數
stmt.setString(1, "張三");
//執行存儲過程
rs = stmt.executeQuery();
while (rs.next()) {
System.out.println("ID = " + rs.getInt(1) + ", NAME = " + rs.getString(2) + ", Email = " +
rs.getString(3) + ", Country = " + rs.getString(4) + ", Password = " + rs.getString(5));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
rs.close();
stmt.close();
conn.close();
}
}
}
結果:
ID = 1, NAME = 張三, Email = [email protected], Country = china, Password = 123456
3.3.2、帶有輸出參數(OUT)的存儲過程
DELIMITER $$
USE `lkf_db`$$
CREATE PROCEDURE `proc_getUserNumByName` (IN p_name VARCHAR(30),OUT p_userNum INT)
BEGIN
SELECT COUNT(id) FROM users WHERE NAME=p_name INTO p_userNum;
END$$
DELIMITER ;
以下示例,將調用具有輸入和輸出參數的存儲過程 proc_getUserNumByName 獲取滿足指定條件的用戶數量。
private final String proc_out_select_sql = "call proc_getUserNumByName(?,?)";
/**
* 帶有輸出參數(OUT)的存儲過程
*/
public void procOutParam() throws SQLException {
Connection conn = null;
CallableStatement stmt = null;
ResultSet rs = null;
try {
//獲取連接對象
conn = DriverManager.getConnection(jdbcUrl, username, password);
//預編譯
stmt = conn.prepareCall(proc_out_select_sql);
//傳遞輸入參數
stmt.setString(1, "張三");
//設置輸出參數(註冊輸出參數)
/**
* 參數一: 參數位置
* 參數二: 存儲過程中的輸出參數的jdbc類型
*/
stmt.registerOutParameter(2, Types.INTEGER);
//執行存儲過程
rs = stmt.executeQuery();
/**
* 得到輸出參數的值
* 索引值: 預編譯sql中的輸出參數的位置
* getXX方法專門用於獲取存儲過程中的輸出參數
*/
Integer result = stmt.getInt(2);
System.out.println(result);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
rs.close();
stmt.close();
conn.close();
}
}
}