mybatis调用存储过程的out参数必须绑定到可修改对象(如预初始化map或含setter的实体类),xml中需用statementtype="callable"并声明mode="out"及jdbctype;不可用基本类型或非null不可变对象直接接收。

MyBatis 调用存储过程时,若存储过程有 OUT 参数(如 Oracle 的 OUT、MySQL 的 OUT 或 SQL Server 的输出参数),不能直接用普通字段赋值,必须显式声明为可修改的容器类型——推荐使用 Map 或自定义实体类配合 @Param + resultMap,但核心前提是:OUT 参数必须绑定到一个可被 MyBatis 修改的对象属性上。
✅ 正确做法:用 Map 接收 OUT 参数(最常用)
MyBatis 会自动将 OUT 参数的值写入 Map 对应 key 的 value 中(前提是该 key 已存在且为可变对象,如 new HashMap())。
- Mapper 接口方法需传入一个预初始化的
Map(不能是空构造后未 put 的 Map,否则 MyBatis 找不到目标 key) - XML 中
<parametermap></parametermap>已废弃,统一用<select></select>或<update></update>的statementType="CALLABLE"+<paramter></paramter>子标签声明 OUT 参数 -
mode="OUT"的参数必须指定jdbcType(如JDBCType.VARCHAR),且javaType通常可省略(MyBatis 自动推断)
示例(Oracle 存储过程 proc_get_user_by_id(id IN NUMBER, name OUT VARCHAR2)):
// Java 调用
Map<string object> params = new HashMap();
params.put("id", 123);
params.put("name", null); // 必须先 put,key 名与 XML 中 property 一致
<p>userMapper.callGetUserByName(params);</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2773" title="Java Development Manual"><img
src="https://img.php.cn/upload/skill/000/000/081/178936208989088.jpg" alt="Java Development Manual" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill2773" title="Java Development Manual" class="overflowclass">Java Development Manual</a>
<p class="overflowclass">Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...</p>
</div>
<a rel="nofollow" href="/xiazai/skill2773" title="Java Development Manual" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>String resultName = (String) params.get("name"); // ✅ 获取 OUT 值
</p></string>
<!-- Mapper XML -->
<select id="callGetUserByName" statementtype="CALLABLE">
{ CALL proc_get_user_by_id(
#{id, mode=IN, jdbcType=NUMERIC},
#{name, mode=OUT, jdbcType=VARCHAR}
) }
</select>
✅ 实体类接收 OUT 参数(需保证属性可写)
实体类方式本质仍是“引用传递”,MyBatis 会通过反射调用 setter 或直接设 field(取决于配置),因此要求:
- 实体类字段必须有 public setter,或开启
configuration.setMapUnderscoreToCamelCase(true)并确保命名匹配 - 调用前必须 new 出实例,并确保 OUT 字段初始值不为 final / 不可变(如 String 可为 null,List 建议 new ArrayList())
- XML 中
property名必须与实体类字段/Setter 名完全一致(区分大小写)
示例:
public class UserResult {
private Integer id;
private String name;
// getter/setter...
}
UserResult result = new UserResult(); result.setId(123); userMapper.callGetUserByName(result); // 传入实例,MyBatis 写入 name 字段 <p>String name = result.getName(); // ✅ 获取 OUT 结果 </p>
<select id="callGetUserByName" statementtype="CALLABLE">
{ CALL proc_get_user_by_id(
#{id, mode=IN, jdbcType=NUMERIC},
#{name, mode=OUT, jdbcType=VARCHAR}
) }
</select>
⚠️ 常见错误和注意事项
- 不能用基本类型(int/long)或不可变对象(如 String 非 null 初始值)直接接收 OUT —— MyBatis 无法修改其值,只会替换引用,原变量不变
- MySQL 存储过程的 OUT 参数实际是用户变量(
@var),MyBatis 不直接支持;建议改用SELECT @out_var AS out_name方式转为结果集,或用CallableStatement原生调用 - SQL Server 需在 JDBC URL 加
sendStringParametersAsUnicode=false避免 NVARCHAR 乱码,且 OUT 参数要显式注册registerOutParameter(2, Types.VARCHAR)(MyBatis 内部已处理,但需确保驱动版本兼容) - 多个 OUT 参数时,Map/实体类中所有对应 key/field 都必须提前存在,否则抛
BindingException
? 小技巧:混合使用 IN/OUT/INOUT
一个存储过程可同时含多种模式参数,写法一致:
<select id="procMixed" statementtype="CALLABLE">
{ CALL my_proc(
#{inId, mode=IN, jdbcType=INTEGER},
#{outName, mode=OUT, jdbcType=VARCHAR},
#{inoutCount, mode=INOUT, jdbcType=INTEGER}
) }
</select>
调用前在 Map 或实体类中同时设置 inId、outName(可为 null)、inoutCount(必须有初值),执行后三者都会被更新。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










