mybatis调用mysql存储过程必须设statementtype="callable",out参数需显式声明mode=out及jdbctype,返回值通过map或实体类字段回填;mysql不支持return语句,仅通过out参数或单行select输出结果。

必须用 CALLABLE 声明语句类型,且输出参数要显式标注 mode=OUT,否则 MyBatis 无法绑定返回值。
MyBatis 调用带 RETURN 值的存储过程(MySQL)
MySQL 存储过程本身不支持标准 SQL 的 RETURN 语句(它只支持 SELECT 或 OUT 参数),所谓“返回值”实际是通过 OUT 参数或 SELECT 单行单列结果实现的。MyBatis 只能捕获这两种形式:
-
OUT参数需在 XML 中用#{paramName,mode=OUT,jdbcType=XXX}显式声明,JDBC 驱动才肯把值回填进 Map 或实体字段 - 若存储过程末尾有
SELECT xxx;(且仅一行一列),可用<select></select>标签 +resultType接收,但此时不能同时定义OUT参数 - 不要用
<update></update>标签处理带SELECT返回的场景——它会忽略结果集,只返回影响行数
Java 层传参必须用 Map 或带 getter/setter 的实体类
MyBatis 要求调用存储过程的参数容器必须是可变对象,以便运行时注入 OUT 值:
- 用
HashMap最直接:提前put("result", null)占位,调用后map.get("result")就是返回值 - 用自定义实体类也行,但该类必须有对应字段(如
private Integer result;)和 public setter,且 XML 中#{result,mode=OUT,...}的 key 名必须与字段名一致 - 别传
Integer、String这类不可变对象作参数——MyBatis 没法修改它的值,返回值会丢失
MySQL 存储过程里别漏写 DELIMITER 和 OUT 声明
常见报错 java.sql.SQLException: Parameter index out of range 往往源于 SQL 定义不合规:
- 创建时必须用
DELIMITER $$(或其他非分号符号),否则 MySQL 会在第一个;就终止解析,导致存储过程体被截断 -
OUT参数必须在声明时明确写出,例如:CREATE PROCEDURE proc_name(IN id INT, OUT code INT),不能只靠 Java 层“约定” - 如果过程内用了
SELECT返回结果集,又同时定义了OUT参数,MyBatis 默认只取第一个结果集(即SELECT那个),OUT值仍需靠参数 Map 获取——两者不冲突,但得分别处理
JdbcTemplate 不适合 RETURN 场景,除非你手动处理 CallableStatement
NamedParameterJdbcTemplate 和普通 JdbcTemplate 都不原生支持 OUT 参数绑定。它们能跑通只是因为 JDBC 底层允许,但你需要自己:
- 用
JdbcTemplate#execute(String sql, PreparedStatementCallback)回调,在回调里强转为CallableStatement - 手动调用
cs.registerOutParameter(2, Types.INTEGER)和cs.getInt(2) - 这比 MyBatis 多写 5–8 行胶水代码,且易出错——比如参数索引从 1 开始、类型要匹配
java.sql.Types常量
真正容易被忽略的是:MySQL 的 OUT 参数值,只有在 cs.execute() 成功返回后才有效;如果过程内部发生异常(比如除零、空指针),OUT 参数不会被赋值,Map 里对应 key 的值仍是初始值(null 或默认值),这点必须在业务逻辑里做防御性判断。











