10.3 MyBatis调用存储过程 10.3.1插入数据

10.3 MyBatis调用存储过程

SQL语句在执行的时候需要先编译,然后执行。数据库的存储过程(Procedure)是组为了完成特定功能的SQL语句,编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用存储过程。
使用存储过程可提高数据库执行速度,并且存储过程可以重复使用,从而减少数据库开发人员的工作量。
下面重点介绍如何使用MyBatis调用存储过程。

10.3.1插入数据

测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 取消外键约束
SET FOREIGN_KEY_CHECKS = 0;
# 创建数据表
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(18) DEFAULT NULL,
`sex` char(2) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# 插入数据
INSERT INTO `tb_user` VALUES ('1', '小明', '男', '21');
INSERT INTO `tb_user` VALUES ('2', '小王', '男', '22');
INSERT INTO `tb_user` VALUES ('3', '小丽', '女', '18');
INSERT INTO `tb_user` VALUES ('4', '小芳', '女', '18');
# 开启外键约束
SET FOREIGN_KEY_CHECKS = 1;

创建存储过程

在之前创建的mybatis数据库中创建一个插入tb_user表数据的存储过程,SQL脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
drop procedure if exists insert_user;
DELIMITER $$
CREATE PROCEDURE insert_user (
OUT v_id int,
in v_name varchar(18),
in v_sex varchar(19),
in v_age int
)
BEGIN
INSERT INTO tb_user (name,sex,age) VALUES (v_name,v_sex,v_age);
SET v_id=LAST_INSERT_ID();
END
$$
DELIMITER ;

存储过程代码详解

以上存储过程代码解释如下:

  • DELIMITERMySQL解释器,用于规定SQL语句的结束符,默认情况SQL语句的结束符是分号(;).为了录入存储过程中的SQL语句,我们需要先把默认的结束符(;)换成其他字符,录入结束后再把结束符换回分号.
  • 在存储过程中使用参数时,除了参数名和参数类型之外,还建议指定参数的mode(模式),可选值为INOUTINOUT三种。
    • 输入参数使用IN,
    • 输出参数(返回)使用OUT,
    • 输入输出参数使用 INOUT
  • LAST_INSERT_IDMySQL的函数,和AUTO_INCREMENT属性一起使用,当往带有AUTO_INCREMENT属性字段的表中新增数据时, LAST_INSERT_ID函数返回该字段的值。

命令行调用存储过程

调用示例如下:

1
call insert_user(@output,'小王','男',22);

第一个参数@output是一个变量,用来接收存储过程的输出.
其三个参数是存储过程的输入.

显示存储过程的输出

select 输出变量名;即可显示存储过程的输出,如下代码所示:

1
select @output;

运行结果如下:

1
2
3
4
5
6
7
8
9
mysql> call insert_user(@output,'小王','男',22);
Database changed
mysql> select @output;
+---------+
| @output |
+---------+
| 5 |
+---------+
1 row in set

此时tb_user表中的数据如下:

1
2
3
4
5
6
7
8
9
10
11
mysql> select * from tb_user;
+----+------+-----+-----+
| id | name | sex | age |
+----+------+-----+-----+
| 1 | 小明 | 男 | 21 |
| 2 | 小王 | 男 | 22 |
| 3 | 小丽 | 女 | 18 |
| 4 | 小芳 | 女 | 18 |
| 5 | 小王 | 男 | 22 |
+----+------+-----+-----+
5 rows in set

创建持久化对象

/MyProcedureTest/src/domain/User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package domain;
public class User {
private Integer id;
private String name;
private String sex;
private Integer age;
public User()
{
super();
}
// 此处省略getter和setter方法,请自己补上
@Override
public String toString()
{
return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
}

编写Mapper.xml映射文件

<insert
    id="inserUser"
    parameterType="domain.User"
    statementType="CALLABLE"> {
    call insert_user(
        #{id,mode=OUT,jdbcType=INTEGER},
        #{name,mode=IN},
        #{sex,mode=IN},
        #{age,mode=IN}
    ) }
</insert>

insert标签调用名为”insert_user“的存储过程完成一个插入数据的操作。

调用存储过程时,需要把statementType属性的值设置为”CALLABLE“。

call“是用来调用存储过程的关键字,
需要注意的是,OUT模式的参数必须指定jdbcType,这是因为在IN模式下,MyBatis提供了默认的jdbcType,而在OUT模式下没有提供。

注意id列的参数jdbcType不要写成javaType.

编写mapper接口

/MyProcedureTest/src/mapper/UserMapper.java
1
2
3
4
5
package mapper;
import domain.User;
public interface UserMapper {
void inserUser(User user);
}

编写测试类

/MyProcedureTest/src/test/InserTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package test;
import domain.User;
import org.apache.ibatis.session.SqlSession;
import fractory.SqlSessionFratoryTools;
import mapper.UserMapper;
public class InserTest {
public static void main(String[] args)
{
SqlSession sqlSession = null;
// 1.获取SqlSession实例
sqlSession = SqlSessionFratoryTools.getSqlSession();
try
{
// 2.获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setAge(23);
user.setName("小李");
user.setSex("男");
// 4.调用mapper接口方法进行操作
userMapper.inserUser(user);
// 5.提交事务
sqlSession.commit();
System.out.println("自动生成的Id:" + user.getId());
} catch (Exception e)
{
// 6.出错回滚
sqlSession.rollback();
e.printStackTrace();
} finally
{
// 7.关闭会话
if (sqlSession != null)
{
sqlSession.close();
}
}
}
}

运行效果

此时tb_user表中的数据如下:

1
2
3
4
5
6
7
8
9
10
11
mysql> select * from tb_user;
+----+------+-----+-----+
| id | name | sex | age |
+----+------+-----+-----+
| 1 | 小明 | 男 | 21 |
| 2 | 小王 | 男 | 22 |
| 3 | 小丽 | 女 | 18 |
| 4 | 小芳 | 女 | 18 |
| 5 | 小王 | 男 | 22 |
+----+------+-----+-----+
5 rows in set

运行测试类,inserUser()方法将会插入一个用户记录到数据库表中,并输出自动生成的id值。控制台显示如下:

1
2
3
4
DEBUG [main] ==>  Preparing: { call insert_user( ?, ?, ?, ? ) } 
DEBUG [main] ==> Parameters: 小李(String), 男(String), 23(Integer)
DEBUG [main] <== Updates: 1
自动生成的Id:6

可以看到,MyBatis调用了名为”insert_user“的存储过程,并且传入了4个参数.其中第一个参数id用来接收存储过程的输出.所以尽管我们没有给user对象的id属性赋值,但是存储过程插入成功后,id属性的值变成了6.
查询数据库,可以看到tb_user表中插入了一条新的记录,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> select * from tb_user;
+----+------+-----+-----+
| id | name | sex | age |
+----+------+-----+-----+
| 1 | 小明 | 男 | 21 |
| 2 | 小王 | 男 | 22 |
| 3 | 小丽 | 女 | 18 |
| 4 | 小芳 | 女 | 18 |
| 5 | 小王 | 男 | 22 |
| 6 | 小李 | 男 | 23 |
+----+------+-----+-----+
6 rows in set