MyBatis 调用存储过程接收 OUTPUT 输出参数
·
前言
在实际企业开发中,经常会遇到使用存储过程处理复杂业务逻辑的场景(比如多表联查、数据统计、事务性批量操作)。MyBatis 作为主流的 ORM 框架,完美支持存储过程调用,但很多新手对如何接收存储过程的 OUT/INOUT 输出参数感到困惑。
本文将从存储过程编写、MyBatis 配置、参数接收、实战案例全流程讲解,带你彻底掌握 MyBatis 接收存储过程 OUTPUT 参数的方法。
在 Java 企业开发中,MyBatis 调用数据库存储过程接收 OUT/INOUT 输出参数是高频场景,本文基于纯 Java + MyBatis环境,从零到一实现存储过程调用、输出参数接收,包含 MySQL 存储过程编写、Mapper 接口、XML 配置、Java 测试全流程,代码可直接复制运行。
一、核心要点(必看)
- MyBatis 调用存储过程必须配置:
statementType="CALLABLE" - 输出参数用
mode=OUT声明,必须指定jdbcType - Java 接收方式:实体类 / Map 集合(推荐实体类,更规范)
- 存储过程执行后,输出参数会自动回写到入参对象中
二、环境准备
- MySQL 5.7/8.0
- MyBatis 3.x
- JDK 1.8+
- 测试表:
sql
CREATE TABLE `user` (
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`age` INT COMMENT '年龄'
);
-- 插入测试数据
INSERT INTO `user`(username, age) VALUES ('张三', 22), ('李四', 25);
三、创建 MySQL 存储过程(带 IN/OUT 参数)
创建一个根据用户 ID 查询信息,并返回输出参数的存储过程:
- 入参:
p_id(用户 ID) - 出参:
p_username(用户名)、p_age(年龄)、p_msg(执行提示)
sql
-- 创建存储过程
DELIMITER //
CREATE PROCEDURE proc_get_user(
IN p_id INT, -- 入参:用户ID
OUT p_username VARCHAR(50), -- 出参:用户名
OUT p_age INT, -- 出参:年龄
OUT p_msg VARCHAR(100) -- 出参:执行结果
)
BEGIN
-- 查询用户数据赋值给输出参数
SELECT username, age INTO p_username, p_age FROM `user` WHERE id = p_id;
-- 判断是否查询到数据
IF p_username IS NOT NULL THEN
SET p_msg = CONCAT('查询成功,用户ID:', p_id);
ELSE
SET p_msg = CONCAT('用户ID:', p_id, ' 不存在');
SET p_age = 0;
END IF;
END //
DELIMITER ;
四、Java 代码实现
1. 实体类(接收输入 / 输出参数)
用于封装入参和出参,存储过程执行后,输出参数会自动赋值到该对象:
java
运行
/**
* 用户参数实体(接收存储过程IN/OUT参数)
*/
public class UserParam {
// 入参:用户ID
private Integer id;
// 出参:用户名
private String username;
// 出参:年龄
private Integer age;
// 出参:执行结果信息
private String msg;
// getter/setter 必须写,MyBatis需要反射赋值
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
public String getUsername() {return username;}
public void setUsername(String username) {this.username = username;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
public String getMsg() {return msg;}
public void setMsg(String msg) {this.msg = msg;}
// 重写toString,方便打印结果
@Override
public String toString() {
return "UserParam{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
", msg='" + msg + '\'' +
'}';
}
}
2. Mapper 接口(定义调用方法)
java
运行
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
/**
* 调用存储过程,接收OUT输出参数
* @param userParam 入参+出参封装对象
*/
void callUserProc(UserParam userParam);
}
3. MyBatis XML 映射文件(核心配置)
关键:statementType="CALLABLE" + mode=OUT 声明输出参数
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 对应Mapper接口全路径 -->
<mapper namespace="com.mapper.UserMapper">
<!-- 调用存储过程:接收OUT参数 -->
<select id="callUserProc" statementType="CALLABLE" parameterType="com.entity.UserParam">
{
CALL proc_get_user(
#{id, mode=IN, jdbcType=INTEGER},
#{username, mode=OUT, jdbcType=VARCHAR},
#{age, mode=OUT, jdbcType=INTEGER},
#{msg, mode=OUT, jdbcType=VARCHAR}
)
}
</select>
</mapper>
4. Java 测试代码(调用 + 接收结果)
直接运行,即可获取存储过程的OUT 输出参数:
java
运行
import com.entity.UserParam;
import com.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class ProcTest {
public static void main(String[] args) throws IOException {
// 1. 加载MyBatis配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2. 获取SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 3. 获取Mapper接口
UserMapper userMapper = session.getMapper(UserMapper.class);
// 4. 封装入参
UserParam param = new UserParam();
param.setId(1); // 查询ID=1的用户
// 5. 调用存储过程(执行后,输出参数自动回写到param对象中)
userMapper.callUserProc(param);
// 6. 接收并打印OUT输出参数
System.out.println("=====存储过程执行结果=====");
System.out.println("用户名:" + param.getUsername());
System.out.println("年龄:" + param.getAge());
System.out.println("执行信息:" + param.getMsg());
System.out.println("完整对象:" + param);
}
}
}
五、执行结果
plaintext
=====存储过程执行结果=====
用户名:张三
年龄:22
执行信息:查询成功,用户ID:1
完整对象:UserParam{id=1, username='张三', age=22, msg='查询成功,用户ID:1'}
六、进阶:Map 接收 OUT 参数(简化版)
如果不想创建实体类,可直接用Map接收,无需定义实体:
1. Mapper 接口
java
运行
void callUserProcMap(Map<String, Object> paramMap);
2. XML 配置
xml
<select id="callUserProcMap" statementType="CALLABLE" parameterType="map">
{
CALL proc_get_user(
#{id, mode=IN, jdbcType=INTEGER},
#{username, mode=OUT, jdbcType=VARCHAR},
#{age, mode=OUT, jdbcType=INTEGER},
#{msg, mode=OUT, jdbcType=VARCHAR}
)
}
</select>
3. Java 测试
java
运行
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", 1);
userMapper.callUserProcMap(paramMap);
// 接收OUT参数
System.out.println("用户名:" + paramMap.get("username"));
System.out.println("执行信息:" + paramMap.get("msg"));
七、常见坑(避坑指南)
- 必须加
statementType="CALLABLE":否则 MyBatis 会把存储过程当普通 SQL 执行,报错 - OUT 参数必须指定
jdbcType:不指定会报类型不匹配错误 - 参数顺序必须和存储过程一致:入参、出参顺序不能乱
- 输出参数自动回写:无需接收返回值,直接从入参对象 / Map 中取
- Oracle 注意:输出参数游标需指定
jdbcType=CURSOR,javaType=ResultSet
八、总结
- MyBatis 调用存储过程核心:
statementType="CALLABLE" - 接收 OUT 参数:
mode=OUT+jdbcType - Java 接收方案:实体类(规范) / Map(简洁)
- 执行后输出参数自动回写到入参对象,直接取值即可
更多推荐




所有评论(0)