MyBatis 執行預存程序,資料已成功寫入資料庫,但 Java 端拋出參數設定異常與空指針異常 (NPE)。常見錯誤堆疊:
java.sql.SQLException: Invalid parameter index
at com.mysql.jdbc.SQLError.createSQLException...
Caused by: java.lang.NullPointerException
at org.apache.ibatis.type.BaseTypeHandler.setParameter...
MyBatis 對預存程序的 OUT 參數需在 <select> 或 <insert> 中使用 mode="OUT" 並指定 jdbcType,否則驅動程式不知道要註冊哪個索引為輸出參數。
若預存程序內有 SELECT 陳述式產生結果集,MyBatis 預設嘗試將結果集映射到 ResultHandler 或 List<T>,但呼叫端若未正確處理 ResultSet 關閉,可能導致連線狀態異常,進而觸發 NPE。
Java 端傳遞的參數個數、順序、類型必須與資料庫端 CREATE PROCEDURE 定義完全一致。多一個、少一個、或 IN/OUT 屬性相反都會導致「Invalid parameter index」。
SET GLOBAL general_log = 'ON'; 查看 CALL proc(...) 實際參數。DBMS_MONITOR 或 SQL Trace。RPC:Completed。比對資料庫收到的參數個數、順序、類型與 Java 端 Mapper 定義是否一致。
<select id="callProc" statementType="CALLABLE">
{CALL my_proc(
#{param1, mode=IN, jdbcType=VARCHAR},
#{param2, mode=OUT, jdbcType=INTEGER, javaType=Integer},
#{param3, mode=INOUT, jdbcType=VARCHAR}
)}
</select>
mode 與 jdbcType。javaType 僅在 OUT/INOUT 且需映射到特定包裝類型時需要(如 Integer vs int)。Map params = new HashMap<>();
params.put("param1", "inputValue");
params.put("param2", null); // OUT 參數必須放入 key,值可為 null
params.put("param3", "inoutValue");
mapper.callProc(params);
// 執行後 params.get("param2") 即為 OUT 回傳值
缺少 key 或 value 為 null 但模式為 IN 會導致 NPE。
CALL proc(...) 驗證程序本身正確。CallableStatement 呼叫,排除 MyBatis 映射層問題。| 現象 | 可能原因 | 修正 |
|---|---|---|
| Invalid parameter index | 參數個數/順序不匹配、OUT 參數未註冊 | 核對 Mapper XML 與 DB 定義,補齊 mode=OUT |
| NPE at BaseTypeHandler.setParameter | IN 參數值為 null 但 jdbcType 不支援 null | 補上 jdbcType=NULL 或確保傳非 null 值 |
| 資料寫入成功但報錯 | 預存程序有 SELECT 產生結果集未處理 | Mapper 加 resultSets 或改用 @Options(statementType=CALLABLE) 配合多結果集處理 |
| OUT 參數取不到值 | 呼叫端 Map 缺少對應 key | 確保 params.put("outParam", null) 存在 |
MyBatis 呼叫預存程序報「參數設定異常」或 NPE,90% 是OUT/INOUT 參數未正確註冊 mode/jdbcType、參數順序個數不匹配、或呼叫端 Map 缺少 OUT 參數 key。建議先在資料庫端追蹤實際 CALL 陳述式,再對照 Mapper XML 逐項核對,並以純 JDBC 隔離驗證。