输入输出参数
预计阅读时间: 约 8 分钟
存储过程的核心优势在于双向数据交互:既可以向数据库传入计算参数,又能将处理结果通过输出参数返回。MyBatis 提供了完善的参数模式支持,通过 mode 属性精确控制每个参数的数据流向。
一、IN / OUT / INOUT 参数详解
1.1 三种参数模式
| 模式 | 方向 | 说明 | 典型场景 |
|---|---|---|---|
IN | Java → DB | 传入参数值,存储过程只读 | 查询条件、过滤参数 |
OUT | DB → Java | 存储过程写入,Java 读取结果 | 统计值、返回码、错误信息 |
INOUT | 双向 | 传入初始值,存储过程修改后返回 | 累加器、分页游标 |
1.2 IN 参数(输入)
最常见的参数模式,等同于普通查询参数:
XML
<select id="getUserCountByDept" statementType="CALLABLE">
{call get_user_count(
#{deptId, mode=IN, jdbcType=INTEGER}
)}
</select>
1.3 OUT 参数(输出)
需要配合 Map 或 JavaBean 传递,调用后从参数对象中获取结果:
XML
<select id="calcOrderTotal" statementType="CALLABLE" parameterType="map">
{calc_order_total(
#{customerId, mode=IN, jdbcType=INTEGER},
#{totalAmount, mode=OUT, jdbcType=DECIMAL},
#{orderCount, mode=OUT, jdbcType=INTEGER},
#{errorCode, mode=OUT, jdbcType=VARCHAR}
)}
</select>
Java 调用:
Java
Map<String, Object> params = new HashMap<>();
params.put("customerId", 1001);
session.selectOne("calcOrderTotal", params);
// 调用后从 Map 中获取 OUT 参数
BigDecimal totalAmount = (BigDecimal) params.get("totalAmount");
Integer orderCount = (Integer) params.get("orderCount");
String errorCode = (String) params.get("errorCode");
1.4 INOUT 参数(双向)
参数值传入后可能被存储过程修改,修改后的值写回原变量:
XML
<select id="paginateResults" statementType="CALLABLE" parameterType="map">
{call paginate(
#{pageSize, mode=IN, jdbcType=INTEGER},
#{offset, mode=INOUT, jdbcType=INTEGER}
)}
</select>
Java
Map<String, Object> params = new HashMap<>();
params.put("pageSize", 20);
params.put("offset", 0); // 初始偏移量
session.selectOne("paginateResults", params);
// offset 已被存储过程更新为下一页起始值
Integer nextOffset = (Integer) params.get("offset");
二、resultMap 映射存储过程结果
2.1 存储过程返回结果集
当存储过程包含 SELECT 语句返回结果集时,使用 resultType 或 resultMap 接收:
XML
<select id="getEmployeeList" statementType="CALLABLE"
resultMap="employeeResultMap">
{call get_dept_employees(#{deptId, mode=IN, jdbcType=INTEGER})}
</select>
<resultMap id="employeeResultMap" type="Employee">
<id property="id" column="emp_id"/>
<result property="name" column="emp_name"/>
<result property="salary" column="emp_salary"/>
<result property="deptName" column="dept_name"/>
</resultMap>
2.2 同时返回结果集和 OUT 参数
存储过程可以既返回结果集又设置 OUT 参数,MyBatis 会同时处理两者:
XML
<select id="getPagedUsers" statementType="CALLABLE"
parameterType="map" resultMap="userResultMap">
{call get_paged_users(
#{pageNum, mode=IN, jdbcType=INTEGER},
#{pageSize, mode=IN, jdbcType=INTEGER},
#{totalCount, mode=OUT, jdbcType=INTEGER}
)}
</select>
Java
Map<String, Object> params = new HashMap<>();
params.put("pageNum", 1);
params.put("pageSize", 10);
// 返回值是结果集列表,OUT 参数写入 params
List<User> users = session.selectList("getPagedUsers", params);
Integer totalCount = (Integer) params.get("totalCount");
2.3 多结果集处理
部分数据库支持存储过程返回多个结果集,使用 <resultMap> 嵌套映射:
XML
<select id="getOrderDetails" statementType="CALLABLE"
parameterType="map" resultMap="orderResultMap">
{call get_order_with_items(
#{orderId, mode=IN, jdbcType=INTEGER}
)}
</select>
<resultMap id="orderResultMap" type="Order">
<id property="id" column="order_id"/>
<result property="orderDate" column="order_date"/>
<collection property="items" ofType="OrderItem">
<id property="id" column="item_id"/>
<result property="productName" column="product_name"/>
<result property="quantity" column="quantity"/>
</collection>
</resultMap>
三、参数模式设置注意事项
3.1 必填属性检查清单
| 参数模式 | 必填属性 | 常见遗漏 |
|---|---|---|
| IN | mode=IN(可选,默认即为 IN) | 无 |
| OUT | mode=OUT + jdbcType | 忘记写 jdbcType 导致类型推断失败 |
| INOUT | mode=INOUT + jdbcType | 只写了 mode 未指定类型 |
3.2 jdbcType 的重要性
XML
<!-- 错误示例:缺少 jdbcType,当值为 null 时可能报错 -->
<select id="callProc" statementType="CALLABLE">
{call my_proc(#{value, mode=OUT})}
</select>
<!-- 正确示例:显式指定 jdbcType -->
<select id="callProc" statementType="CALLABLE">
{call my_proc(#{value, mode=OUT, jdbcType=VARCHAR})}
</select>
3.3 不同数据库的 OUT 参数差异
| 数据库 | OUT 参数语法 | 注意事项 |
|---|---|---|
| MySQL | OUT param_name TYPE | 支持较好 |
| Oracle | param_name OUT TYPE | 需注意 PL/SQL 块格式 |
| PostgreSQL | OUT param_name TYPE | 使用 CREATE FUNCTION 风格 |
| SQL Server | 使用 OUTPUT 关键字 | 需用 EXEC 调用 |
四、要点总结
| 要点 | 说明 |
|---|---|
| 三种模式 | IN 单向传入、OUT 单向返回、INOUT 双向交互 |
| OUT 参数容器 | 推荐使用 Map 接收,调用后从中取值 |
| resultMap 复用 | 存储过程结果集的映射与普通查询完全一致 |
| jdbcType 必填 | OUT 和 INOUT 参数必须指定,否则 NULL 值场景会报错 |
| 多结果集 | 通过 collection 嵌套实现一对多映射 |
📝 发现内容有误?点击此处直接编辑