必须加statementType="CALLABLE",否则MyBatis按普通SQL解析导致报错或静默失败;IN/OUT参数需显式声明mode且全大写;游标调用须同时配置jdbcType=CURSOR、javaType=ResultSet和独立resultMap。

statementType="CALLABLE" 是必须加的,否则 MyBatis 会按普通 SQL 解析,直接报错或静默失败。
必须设 statementType="CALLABLE"
MyBatis 默认把 <select>、<update> 等标签当成普通 SQL 执行。Oracle 存储过程调用语法是 {call proc_name(...)},只有显式声明 statementType="CALLABLE",MyBatis 才会走 JDBC 的 CallableStatement 流程。
常见错误现象:ORA-00900: invalid SQL statement 或 java.sql.SQLException: Invalid column name,往往就是漏了这个属性。
- 所有存储过程调用(无论有无参数、是否返回游标)都必须加
- 不能只写在某个标签里而其他忽略——每个存储过程映射都要单独加
-
<delete>标签也能用,只要加了statementType="CALLABLE",和<select>效果一致
mode=IN 和 mode=OUT 必须显式指定
MyBatis 不会自动推断参数方向。即使 Oracle 存储过程中写了 fid IN INTEGER,MyBatis 仍要求你在 #{...} 中明确写 mode=IN 或 mode=OUT,否则会抛 org.apache.ibatis.executor.ExecutorException: No value supplied for the SQL parameter。
注意大小写:必须全大写 IN/OUT,写成 in 或 out 会静默忽略,导致参数未绑定。
-
IN参数:传入值,Map 中 key 对应的 value 必须有值(如map.put("fid", 123)) -
OUT参数:不需提前 put,MyBatis 执行后会把结果写回 Map 同名 key 下 -
INOUT参数:既传入又接收返回,需提前 put 初始值,执行后覆盖
返回游标必须配 jdbcType=CURSOR + resultMap + javaType=ResultSet
Oracle 的 SYS_REFCURSOR 在 JDBC 层是 ResultSet 类型,但 MyBatis 不会自动转换。漏掉任一配置都会出错:
- 缺
jdbcType=CURSOR→Invalid argument at index 1 - 缺
javaType=java.sql.ResultSet→ClassCastException: OracleResultSetImpl cannot be cast to List - 缺
resultMap或写错 ID → 返回空集合或字段映射全 null
示例片段:
<select id="get_data_by_procedure" statementType="CALLABLE">
{call get_data_by_procedure(
#{fid, mode=IN, jdbcType=NUMERIC},
#{v_cursor, mode=OUT, jdbcType=CURSOR, javaType=java.sql.ResultSet, resultMap=ProcedureInfoMap}
)}
</select>
注意:resultMap 必须是独立定义的,不能复用普通查询的 BaseResultMap,因为游标返回的是动态结果集,字段和类型可能不完全匹配。
参数类型大小写与 JDBC Type 映射要严格匹配
Oracle 的 VARCHAR2 对应 VARCHAR,NUMBER 常对应 NUMERIC 或 INTEGER,但不是所有驱动版本都兼容。最稳妥做法是查你用的 ojdbc 版本文档,或直接试错:
-
jdbcType=VARCHAR比jdbcType=STRING更可靠 -
jdbcType=NUMERIC比jdbcType=DECIMAL更通用(尤其对整数) - 布尔类型在 Oracle 没原生支持,通常用
CHAR(1)+jdbcType=CHAR模拟 - 日期参数建议统一用
jdbcType=DATE,避免TIMESTAMP引发时区问题
一个容易被忽略的点:存储过程里定义的参数类型(如 fid IN INTEGER)只是 PL/SQL 类型,底层 JDBC 绑定看的是 Oracle 字段实际物理类型,不是别名。


















