JDBC 三種語句對象對比
| 類型 | 用途 | 特點 |
| Statement | 靜態 SQL | 無參數,每次編譯執行 |
| PreparedStatement | 參數化 SQL | 預編譯,批量處理,防注入 |
| CallableStatement | 調用存儲過程 | 支持 IN/OUT/INOUT 參數,命名參數 增強靈活性 |
命名參數優勢
- 順序無關:按參數名設置值,無需匹配存儲過程定義順序。
- 可選參數:存儲過程有默認值的參數可省略不傳。
- 健壯性:存儲過程參數順序變更不影響 Java 代碼。
- 可讀性:代碼自文檔化,參數用途一目了然。
驅動支持檢測
DatabaseMetaData meta = conn.getMetaData();
if (meta.supportsNamedParameters()) {
System.out.println("驅動支持命名參數");
}
獲取存儲過程參數元數據
ResultSet rs = meta.getProcedureColumns("myDB", "schema", "procName", "%");
while (rs.next()) {
String paramName = rs.getString("COLUMN_NAME"); // 第 4 列
System.out.println("參數: " + paramName);
}
完整調用示例
存儲過程定義 (Informix)
CREATE PROCEDURE CreateProductDef(
productname VARCHAR(64),
productdesc VARCHAR(64),
listprice FLOAT DEFAULT 100.00,
minprice FLOAT DEFAULT 90.00,
OUT prod_id FLOAT
)
LET prod_id = <生成邏輯>;
END PROCEDURE;
Java 調用代碼
String sql = "{call CreateProductDef(?, ?, ?, ?)}"; // 4 個佔位符對應 5 參數 (listprice 有默認值)
CallableStatement cstmt = conn.prepareCall(sql);
// 命名參數設置 (順序任意)
cstmt.setString("productname", "筆記本電腦");
cstmt.setString("productdesc", "高性能輕薄本");
cstmt.setFloat("minprice", 8999.00f);
// listprice 省略,使用默認值 100.00
// 註冊 OUT 參數
cstmt.registerOutParameter("prod_id", Types.FLOAT);
// 執行
cstmt.execute();
// 獲取 OUT 參數值
float newId = cstmt.getFloat("prod_id");
System.out.println("新產品 ID: " + newId);
INOUT 參數處理
// 存儲過程定義 INOUT
CREATE PROCEDURE UpdatePrice(
INOUT price FLOAT DEFAULT 100.00,
OUT new_price FLOAT
)
// Java 註冊並設置
cstmt.registerOutParameter("price", Types.FLOAT);
cstmt.setFloat("price", 150.00f); // 同時作為 IN 輸入
cstmt.registerOutParameter("new_price", Types.FLOAT);
cstmt.execute();
float updated = cstmt.getFloat("price"); // 獲取 INOUT 更新後值
float result = cstmt.getFloat("new_price"); // 獲取 OUT 值
完整演示程序結構
public class CallableDemo {
static Connection conn;
public static void main(String[] args) {
getConnection();
createStoredProcedure();
testNamedParameters();
}
// ... 連接、創建過程、測試調用方法
}
最佳實踐總結
- 優先使用命名參數調用存儲過程,提升維護性。
- 利用默認值減少必傳參數,簡化調用代碼。
- OUT/INOUT 參數必須
registerOutParameter 註冊類型。
- 異常處理包裹
execute(),確保資源關閉。
- 驅動不支持命名參數時退回到位置參數 (索引從 1 開始)。