MyBatis 入门教程之八

大纲

MyBatis 实用场景

MyBatis 批量操作

调用不带参数的 openSession() 方法时,创建的 SqlSession 对象具有如下特性:

  • 会开启一个事务(不会自动提交)
  • 数据库连接对象会从由环境配置的数据源实例得到
  • 事务隔离级别将会使用驱动或数据源的默认配置
  • 预处理语句不会被复用,也不会批量处理更新

值得一提的是,openSession() 方法的 ExecutorType 类型的参数是枚举类型,取值如下:

  • SIMPLE:这个执行器类型不做特殊的事情(默认执行器),它会为每个 SQL 语句的执行创建一个新的预处理语句。
  • REUSE:这个执行器类型会复用预处理语句。
  • BATCH:这个执行器会批量执行所有更新语句。

批量操作介绍

批量操作就是使用 MyBatis 提供的 BATCH 类型的 Executor 进行的,它的底层是通过 JDBC 暂存 SQL 语句的方式来实现,也就是保存 SQL 语句到一定数量后再发给数据库执行一次。MyBatis 与 Spring 整合时,推荐额外配置一个可以专门用来执行批量操作的 SqlSession,配置示例如下。当需要使用批量操作的时候,可以在 Service 层注入可批量操作的 SqlSession,然后通过它获取 Mapper 映射器来操作数据库。

1
2
3
4
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="executorType" value="BATCH" />
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean" />
</bean>

特别注意

  1. 当 ExecutorType 指定为 BATCH 后,执行增删改操作不会再返回正确的受影响的记录数。
  2. 批量操作是在 session.commit() 执行以后才发送 SQL 语句给数据库执行的。若想让其提前执行,以方便后续的查询操作可以获取到数据,则可以调用 sqlSession.flushStatements() 方法,让其直接冲刷到数据库进行执行。

批量操作使用案例

本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-22

  • Mapper 接口
1
2
3
4
5
public interface EmployeeMapper {

public boolean addEmp(Employee employee);

}
  • SQL 映射文件
1
2
3
4
5
6
7
8
<mapper namespace="com.clay.mybatis.dao.EmployeeMapper">

<insert id="addEmp" parameterType="com.clay.mybatis.bean.Employee">
insert into t_employee (last_name, gender, email)
values(#{lastName}, #{gender}, #{email})
</insert>

</mapper>
  • 批量操作
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
public class MyBatisApplication {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 批量操作:设置执行器的类型为 BATCH
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);

for (int i = 0; i < 10; i++) {
Employee employee = new Employee("employe-" + i, "1", "employe@gmail.com");
mapper.addEmp(employee);
}

session.commit();
} finally {
if (session != null) {
session.close();
}
}
}

}
  • 执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
21:25:28,897 DEBUG JdbcTransaction:137 - Opening JDBC Connection
21:25:29,120 DEBUG PooledDataSource:434 - Created connection 706895319.
21:25:29,120 DEBUG JdbcTransaction:101 - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2a225dd7]
21:25:29,124 DEBUG addEmp:137 - ==> Preparing: insert into t_employee (last_name, gender, email) values(?, ?, ?)
21:25:29,165 DEBUG addEmp:137 - ==> Parameters: employe-0(String), 1(String), employe@gmail(String)
21:25:29,166 DEBUG addEmp:137 - ==> Parameters: employe-1(String), 1(String), employe@gmail(String)
21:25:29,166 DEBUG addEmp:137 - ==> Parameters: employe-2(String), 1(String), employe@gmail(String)
21:25:29,167 DEBUG addEmp:137 - ==> Parameters: employe-3(String), 1(String), employe@gmail(String)
21:25:29,167 DEBUG addEmp:137 - ==> Parameters: employe-4(String), 1(String), employe@gmail(String)
21:25:29,168 DEBUG addEmp:137 - ==> Parameters: employe-5(String), 1(String), employe@gmail(String)
21:25:29,168 DEBUG addEmp:137 - ==> Parameters: employe-6(String), 1(String), employe@gmail(String)
21:25:29,170 DEBUG addEmp:137 - ==> Parameters: employe-7(String), 1(String), employe@gmail(String)
21:25:29,170 DEBUG addEmp:137 - ==> Parameters: employe-8(String), 1(String), employe@gmail(String)
21:25:29,170 DEBUG addEmp:137 - ==> Parameters: employe-9(String), 1(String), employe@gmail(String)
21:25:29,174 DEBUG JdbcTransaction:70 - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2a225dd7]
21:25:29,177 DEBUG JdbcTransaction:123 - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2a225dd7]
21:25:29,177 DEBUG JdbcTransaction:91 - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2a225dd7]
21:25:29,178 DEBUG PooledDataSource:391 - Returned connection 706895319 to pool.

观察上面的执行结果,可以发现使用 MyBatis 的批量操作插入多条数据时,不会发送多条 SQL 语句到数据库,这大大提高了业务逻辑的执行效率。

PageHelper 分页插件

PageHelper 是 MyBatis 第三方的分页插件,支持任何复杂的单表、多表分页查询,部分特殊情况请看重要提示

PageHelper 官方文档

PageHelper 使用案例

本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-21

  • 引入依赖
1
2
3
4
5
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.1</version>
</dependency>
  • 注册插件
1
2
3
4
5
6
7
<configuration>

<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>

</configuration>
  • Mapper 接口
1
2
3
4
5
public interface EmployeeMapper {

public List<Employee> getEmps();

}
  • SQL 映射文件

提示

在编写 SQL 语句的时候,不需要手动指定任何分页参数。

1
2
3
4
5
6
7
8
<mapper namespace="com.clay.mybatis.dao.EmployeeMapper">

<select id="getEmps" resultType="com.clay.mybatis.bean.Employee">
select id, last_name as lastName, gender, email
from t_employee
</select>

</mapper>
  • 业务逻辑,PageHelper 更多 API 的使用示例可以看 这里
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
public class MyBatisApplication {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession session = sqlSessionFactory.openSession();
try {
// 设置分页信息
Page<Object> page = PageHelper.startPage(2, 2);

// 数据库查询
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.getEmps();
System.out.println(employees);

// 获取分页信息
PageInfo<Object> pageInfo = page.toPageInfo();
System.out.println("总页数: " + pageInfo.getPages());
System.out.println("总记录数: " + pageInfo.getTotal());
System.out.println("当前的页码: " + pageInfo.getPageNum());
System.out.println("当前的记录数: " + pageInfo.getSize());
System.out.println("每页的记录数: " + pageInfo.getPageSize());
System.out.println("是否有下一页: " + pageInfo.isHasNextPage());
System.out.println("是否为第一页: " + pageInfo.isIsFirstPage());
System.out.println("是否为最后一页: " + pageInfo.isIsLastPage());
} finally {
if (session != null) {
session.close();
}
}
}

}
  • 执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
23:16:35,705 DEBUG getEmps_COUNT:137 - ==>  Preparing: SELECT count(0) FROM t_employee
23:16:35,763 DEBUG getEmps_COUNT:137 - ==> Parameters:
23:16:35,802 DEBUG getEmps_COUNT:137 - <== Total: 1
23:16:35,807 DEBUG getEmps:137 - ==> Preparing: select id, last_name as lastName, gender, email from t_employee LIMIT ?, ?
23:16:35,810 DEBUG getEmps:137 - ==> Parameters: 2(Long), 2(Integer)
23:16:35,812 DEBUG getEmps:137 - <== Total: 1
Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=3, pages=2, reasonable=false, pageSizeZero=false}[4_Tom_1_tom@gmail.com]
总页数: 2
总记录数: 3
当前的页码: 2
当前的记录数: 1
每页的记录数: 2
是否有下一页: false
是否为第一页: false
是否为最后一页: true

MyBatis 调用存储过程

存储过程(Stored Procedure)是一种存储在数据库中的复杂程序,以便外部程序调用的一种数据库对象。存储过程是为了完成特定功能的 SQL 语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名称并给定参数(需要时)来调用执行。存储过程的设计思想很简单,本质就是数据库 SQL 语言层面的代码封装与重用。

存储过程的优缺点

存储过程的优点:

  • 极大地提高 SQL 语言的功能和灵活性
  • 可保证数据的安全性和完整性
    • 通过存储过程可以使相关的动作在一起发生,从而可以维护数据库的完整性
    • 通过存储过程可以使没有权限的用户在控制之下间接地存取数据库,从而保证数据的安全
  • 极大地改善 SQL 语句的性能
    • 在运行存储过程前,数据库已对其进行了语法和句法分析,并给出优化执行方案,这种已经编译好的过程可极大地改善 SQL 语句的性能。由于执行 SQL 语句的大部分解析工作已经完成,所以存储过程能以极快的速度执行
  • 可以降低网络的通信量
    • 客户端调用存储过程时,只需要传存储过程名和相关参数即可,与传输 SQL 语句相比自然数据量少了很多

存储过程的缺点:

  • 开发阶段难以调试
  • 难以移植,存储过程往往定制化于特定的数据库上,当切换到其他厂商的数据库系统时,往往需要重写原有的存储过程
  • 如果在一个系统中大量的使用存储过程,到程序交付使用的时候,随着用户需求的增加会导致数据结构的变化,存储过程也要同步更新。最后如果用户想维护该系统可以说是很难的,而且代价是空前的,维护起来更麻烦

MySQL 存储过程的语法

  • 声明语句结束符
1
2
3
4
5
delimiter $$

或者

delimiter //
  • 声明存储过程
1
create procedure demo_in_parameter(in p_in int) 
  • 存储过程的开始和结束符号
1
begin .... end
  • 变量定义
1
declare l_int int unsigned default 4000000; 
  • 变量赋值
1
set @p_in=1
  • 调用存储过程
1
call demo_in_parameter();
  • 删除存储过程
1
drop procedure demo_in_parameter;
  • 创建存储过程(完整的)
1
2
3
4
5
6
delimiter $$
create procedure datetime()
BEGIN
select now();
END $$
delimiter ;

存储过程调用案例

本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-23

提示

  1. 若需要调用存储过程,可以使用 <select> 标签,并设置标签的 statementType 属性为 CALLABLE 即可
  2. <select> 标签体中存储过程的调用语法: { call procedure_name(#{param1_info}, #{param2_info}) }
MySQL 存储过程调用案例一

在下面的案例中,演示了如何在 MySQL 数据库中使用存储过程来根据员工 ID 查询员工的详细信息。

  • JavaBean 类
1
2
3
4
5
6
7
8
public class Employee {

private Long id;
private String lastName;
private String gender;
private String email;

}
  • Mapper 接口
1
2
3
4
5
public interface EmployeeMapper {

public Employee getEmpById(Long id);

}
  • 创建 MySQL 存储过程
1
2
3
4
5
6
delimiter $$
create procedure query_single_employee(IN empId long)
begin
select id, last_name as lastName, gender, email from t_employee where id = empId;
end $$
delimiter ;
  • SQL 映射文件
1
2
3
4
5
6
7
8
<mapper namespace="com.clay.mybatis.dao.EmployeeMapper">

<!-- 调用存储过程(传入参数) -->
<select id="getEmpById" parameterType="Long" resultType="com.clay.mybatis.bean.Employee" statementType="CALLABLE">
{ call query_single_employee(#{id, mode=IN, jdbcType=BIGINT}) }
</select>

</mapper>
  • 业务逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyBatisApplication {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession session = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1L);
System.out.println(employee);
} finally {
if (session != null) {
session.close();
}
}
}

}
  • 执行结果
1
2
3
4
22:58:51,817 DEBUG getEmpById:137 - ==>  Preparing: { call query_single_employee(?) }
22:58:51,914 DEBUG getEmpById:137 - ==> Parameters: 1(Long)
22:58:51,942 DEBUG getEmpById:137 - <== Total: 1
22:58:51,942 DEBUG getEmpById:137 - <== Updates: 0
Oracle 存储过程调用案例二

MyBatis 对存储过程的游标提供了一个 JdbcType=CURSOR 的支持,可以智能地通过游标读取数据集,并将数据集自动映射到声明的结果集中。在下面的案例中,简单演示了如何在 Oracle 数据库中使用存储过程来实现分页查询的逻辑(这里只是简单演示,并未完全真正实现分页查询的逻辑)。

  • JavaBean 类
1
2
3
4
5
6
7
8
public class Employee {

private Long id;
private String lastName;
private String gender;
private String email;

}
1
2
3
4
5
6
7
8
public class PageEmp {

private int start;
private int end;
private int count;
private List<Employee> emps;

}
  • Mapper 接口
1
2
3
4
public interface EmployeeMapper {

public void getPageByProcedure(PageEmp page);
}
  • 创建 Oracle 存储过程
1
2
3
4
5
6
7
8
9
10
11
12
13
create or replace procedure
page_emp (
p_start in int,
p_end in int,
p_count out int,
ref_cur out sys_refcursor
) AS
BEGIN
select count(*) into p_count from t_employee;
open ref_cur for
select * from (select e.*, rownum as r1 from t_employee e where rownum < p_end)
where r1 > p_start;
END page_emp;
  • SQL 映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<mapper namespace="com.clay.mybatis.dao.EmployeeMapper">

<resultMap id="EmpResultMap" type="com.clay.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
</resultMap>

<!-- 调用存储过程,通过游标读取返回的数据集,并将数据集自动映射到声明的结果集中 -->
<select id="getPageByProcedure" parameterType="com.clay.mybatis.bean.PageEmp" statementType="CALLABLE" databaseId="oracle">
{
call page_emp (
#{start, mode=IN, jdbcType=INTEGER},
#{end, mode=IN, jdbcType=INTEGER},
#{count, mode=OUT, jdbcType=INTEGER},
#{emps, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=EmpResultMap}
)
}
</select>

</mapper>
  • MyBatis 全局配置文件
1
2
3
4
5
<!-- 数据库厂商标识 -->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
  • 业务逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyBatisApplication {

public static void main(String[] args) throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
PageEmp page = new PageEmp();
page.setStart(1);
page.setEnd(5);
mapper.getPageByProcedure(page);

System.out.println("总记录数:" + page.getCount());
System.out.println("查出的数据:" + page.getEmps());
} finally {
if (session != null) {
session.close();
}
}
}

}

MyBatis 枚举类型处理器

默认的枚举处理器

MyBatis 在处理枚举类型对象的时候,默认是使用 EnumTypeHandler 枚举类型处理器将枚举的名称保存到数据库。若希望将枚举类型的索引值保存到数据库,则只需要在 MyBatis 的全局配置文件中指定使用 EnumOrdinalTypeHandler 枚举类型处理器即可,配置示例如下:

1
2
3
4
5
6
7
8
<configuration>

<typeHandlers>
<!-- 让 MyBatis 使用 EnumOrdinalTypeHandler 处理器来处理 AdminStatus 枚举类型 -->
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.clay.mybatis.enums.AdminStatus"/>
</typeHandlers>

</configuration>

自定义枚举类型处理器的步骤

自定义枚举类型处理器的步骤如下:

  • 1、实现 TypeHandler 接口或者继承 BaseTypeHandler
  • 2、使用 @MappedType 注解定义要处理的 javaType 类型,使用 @MappedJdbcTypes 注解定义要处理的 jdbcType 类型,这一步骤可选
  • 3、通过以下三种方式的任意一种来使用自定义的类型处理器
    • 在参数处理的时候,声明要使用自定义的 TypeHandler 进行处理,例如: #{status, typeHandler=xxxx}
    • 在 MyBatis 的全局配置文件中,指定 TypeHandler 要处理的 javaType 类型,例如:<typeHandlers> <typeHandler handler="xxxx" javaType="xxxx"/> </typeHandlers>
    • 在定义结果集标签的时候,声明要使用自定义的 TypeHandler 处理参数的封装,例如:<resultMap> <result column="status" property="status" typeHandler="xxxx"> </result> </resultMap>

自定义枚举类型处理器的案例

本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-lesson-24

  • 创建数据库表
1
2
3
4
5
6
7
CREATE TABLE `t_admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`status` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • JavaBean 类
1
2
3
4
5
6
7
8
public class Admin {

private Long id;
private String name;
private String email;
private AdminStatus status;

}
  • 枚举类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 枚举接口
*/
public interface BaseEnum<E extends Enum<?>, T> {

/**
* 获取枚举值
*
* @return 枚举值
*/
T getValue();

/**
* 获取枚举值描述
*
* @return 枚举值的描述
*/
String getDescription();

}
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
/**
* 枚举实现类
*/
public enum AdminStatus implements BaseEnum<AdminStatus, Integer> {

VALID(1, "有效"), INVALID(0, "无效");

private final Integer value;

private final String description;

@Override
public Integer getValue() {
return this.value;
}

@Override
public String getDescription() {
return this.description;
}

private AdminStatus(Integer value, String description) {
this.value = value;
this.description = description;
}

}
  • 枚举类型处理器
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import com.clay.mybatis.enums.BaseEnum;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/**
* 枚举类型处理类
*/
public class AutoGenericEnumTypeHandler<E extends BaseEnum<?, ?>> extends BaseTypeHandler<E> {

private Class<E> enumType;
private E[] enums;

public AutoGenericEnumTypeHandler() {

}

public AutoGenericEnumTypeHandler(Class<E> type) {
if (Objects.isNull(type)) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.enumType = type;
this.enums = type.getEnumConstants();
if (Objects.isNull(this.enums)) {
throw new IllegalArgumentException(type.getName() + " does not represent an enum type");
}
}

private E loadEnum(Object index) {
for (E e : enums) {
if (e.getValue().toString().equals(index.toString())) {
return e;
}
}
throw new IllegalArgumentException(enumType.getName() + " unknown enumerated type index : " + index);
}

@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setObject(i, parameter.getValue());
}

@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
if (Objects.isNull(rs.getObject(columnName))) {
return null;
}
Object index = rs.getObject(columnName);
return loadEnum(index);
}

@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
if (Objects.isNull(rs.getObject(columnIndex))) {
return null;
}
Object index = rs.getObject(columnIndex);
return loadEnum(index);
}

@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
if (Objects.isNull(cs.getObject(columnIndex))) {
return null;
}
Object index = cs.getObject(columnIndex);
return loadEnum(index);
}

}
  • Mapper 接口
1
2
3
4
5
6
public interface AdminMapper {

public Admin getAdminById(Long id);
public boolean addAdmin(Admin admin);

}
  • SQL 映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<mapper namespace="com.clay.mybatis.dao.AdminMapper">

<select id="getAdminById" parameterType="Long" resultType="com.clay.mybatis.bean.Admin">
select id, name, email, status
from t_admin
where id = #{id}
</select>

<insert id="addAdmin" parameterType="com.clay.mybatis.bean.Admin" useGeneratedKeys="true" keyProperty="id">
insert into t_admin (name, email, status)
values(#{name}, #{email}, #{status})
</insert>

</mapper>
  • MyBatis 的全局配置文件
1
2
3
4
5
6
7
8
<configuration>

<typeHandlers>
<!-- 让 MyBatis 使用 AutoGenericEnumTypeHandler 处理器来处理 AdminStatus 枚举类型 -->
<typeHandler handler="com.clay.mybatis.handler.AutoGenericEnumTypeHandler" javaType="com.clay.mybatis.enums.AdminStatus" />
</typeHandlers>

</configuration>
  • 业务代码
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
public class MyBatisApplication {

public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession session = sqlSessionFactory.openSession();
try {
AdminMapper mapper = session.getMapper(AdminMapper.class);

Admin entity = new Admin("admin", "admin@gmail.com", AdminStatus.VALID);
mapper.addAdmin(entity);

Admin admin = mapper.getAdminById(1L);
System.out.println(admin);

session.commit();
} finally {
if (session != null) {
session.close();
}
}
}

}

  • 执行结果
1
2
3
4
5
6
7
21:36:08,853 DEBUG addAdmin:137 - ==>  Preparing: insert into t_admin (name, email, status) values(?, ?, ?)
21:36:08,892 DEBUG addAdmin:137 - ==> Parameters: admin(String), admin@gmail.com(String), 1(Integer)
21:36:08,893 DEBUG addAdmin:137 - <== Updates: 1
21:36:08,909 DEBUG getAdminById:137 - ==> Preparing: select id, name, email, status from t_admin where id = ?
21:36:08,910 DEBUG getAdminById:137 - ==> Parameters: 1(Long)
21:36:08,927 DEBUG getAdminById:137 - <== Total: 1
Admin [id=1, name=admin, email=admin@gmail.com, status=VALID]

观察上面的执行结果,可以发现 MyBatis 会自动将枚举类型的 value 属性保存到数据库中,而不是保存枚举类型的名称或者索引值,同时查询数据后也会自动封装枚举类型的数据。