MyBatis-Plus 入门教程之五

前言

版本说明

本文的教程内容是基于 MyBatis-Plus 3.5.2 版本讲解的,若你使用的是 2.x 或其他版本,可能会有部分知识点和案例代码不兼容。

MyBatis-Plus 主键生成策略

主键生成策略

  • 使用方式:在实体类的主键属性上添加注解 @TableId(type = IdType.AUTO),或者在全局配置文件中通过 idType 属性指定 MyBatis-Plus 的主键生成策略
主键生成策略描述
AUTO 数据库 ID 自增,该类型必须确保数据库表的主键字段设置了 ID 自增,否则无法生效
NONE 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT 用户自行设置主键值,该类型可以通过自己注册自动填充插件进行填充
ASSIGN_ID 分配 ID,主键类型为 Number [Long 和 Integer] 或 String(@since 3.3.0 version),使用的是 IdentifierGenerator 接口的 nextId 方法(默认实现类为 DefaultIdentifierGenerator,使用的是雪花算法)
ASSIGN_UUID 分配 UUID,主键类型为 String(@since 3.3.0 version),使用的是 IdentifierGenerator 接口的 nextUUID 方法
ID_WORKER 分布式全局唯一 ID 长整型类型(请使用 ASSIGN_ID
UUID32 位 UUID 字符串(请使用 ASSIGN_UUID
ID_WORKER_STR 分布式全局唯一 ID 字符串类型(请使用 ASSIGN_ID

注意

1、AUTO 主键类型必须确保数据库表的主键字段设置了 ID 自增,否则无法生效
2、ASSIGN_ID 主键类型(使用雪花算法生成 ID),与数据库表的主键字段是否设置 ID 自增没有任何关系
3、ASSIGN_IDASSIGN_UUID 这两种主键类型,只有当插入对象的 ID 为空时,才会自动填充主键值

Sequence 序列生成器

对于 INPUT 类型的主键生成策略,一种情况是程序里面自己指定主键,另一种是利用 MyBatis-Plus 内置的序列生成器来生成主键。由于不是所有数据库都像 MySQL 一样支持自增主键,例如在 Oracle 数据库中就不支持主键自增长,它是通过 Sequence 序列来获取主键的,MyBatis-Plus 为了解决该问题提供了序列生成器。MyBatis-Plus 内置支持以下序列生成器,如果内置支持不满足你的需求,可实现 IKeyGenerator 接口来进行扩展。

  • DB2KeyGenerator
  • H2KeyGenerator
  • KingbaseKeyGenerator
  • OracleKeyGenerator
  • PostgreKeyGenerator

Oracle 主键生成策略使用案例

指定 Oracle 主键生成策略的步骤

  • 1、实体类添加 @KeySequence(value = "seq_employee", dbType = DbType.ORACLE) 注解,指定数据库中序列的名称
  • 2、实体类属性添加 @TableId 注解,指定主键生成策略为 IdType.INPUT,或者在配置文件中全局指定 MyBatis-Plus 的主键生成策略为 IdType.INPUT
  • 3、注入 com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator 序列生成器到 Spring 容器中

初始化数据库

1
2
3
4
5
6
7
8
9
10
11
-- 创建表
CREATE TABLE t_employee(id number(11) primary key, last_name varchar(255) DEFAULT NULL, gender char(1) DEFAULT NULL, email varchar(255) DEFAULT NULL, age number DEFAULT NULL);

-- 创建序列
create sequence seq_employee start with 100 increment by 2;

-- 插入数据
insert into t_employee(id, last_name, gender, email, age) values(1, 'Jim','1', 'jim@gmail.com', 26);
insert into t_employee(id, last_name, gender, email, age) values(2, 'Peter','1', 'peter@gmail.com', 29);
insert into t_employee(id, last_name, gender, email, age) values(3, 'David','1', 'david@gmail.com', 28);
insert into t_employee(id, last_name, gender, email, age) values(4, 'Tom','1', 'tom@gmail.com', 25);

引入 Oracle 依赖

由于 Oracle 授权的问题,不能直接从 Maven 的仓库中下载到 Oracle 驱动。为了简单演示,这里直接将驱动包存放在项目的 lib 目录中,然后通过本地文件的方式引用,其他引入方式可以看 这里

1
2
3
4
5
6
7
8
9
10
<!-- Oracle 11g 驱动 -->
<dependencies>
<dependency>
<groupId>oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/ojdbc6.jar</systemPath>
</dependency>
</dependencies>

Oracle 数据库连接信息

1
2
3
4
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@localhost:1521:xe
username: system
password: oracle

@KeySequence 注解指定序列名称

指定序列名称,在实体类上添加 @KeySequence 注解,value 属性是 Oracle 数据库中序列的名称,dbType 属性是数据库的类型,完整的数据库类型列表可查看 DbType 类的源码注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@KeySequence(value = "seq_employee", dbType = DbType.ORACLE)
public class Employee {

private Long id;

private String email;

private String lastName;

private String gender;

private Integer age;

....

}

指定主键生成策略

@TableId 注解方式

指定主键生成策略,在实体类的主键属性上添加 @TableId 注解,type 属性必须为 IdType.INPUT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@KeySequence(value = "seq_employee", dbType = DbType.ORACLE)
public class Employee {

@TableId(type = IdType.INPUT)
private Long id;

private String email;

private String lastName;

private String gender;

private Integer age;

....

}
全局配置文件方式

除了可以使用 @TableId 注解单独指定主键生成策略外,还可以在配置文件中全局指定 MyBatis-Plus 的主键生成策略。

Spring XML 配置文件
1
2
3
4
5
6
7
8
9
10
11
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="globalConfig" ref="globalConfig"></property>
</bean>

<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="dbConfig" />
</bean>

<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<property name="idType" value="INPUT"></property>
</bean>
SpringBoot 配置文件
1
2
3
4
mybatis-plus:
global-config:
db-config:
id-type: INPUT

注入序列生成器

将 Oracle 的序列生成器注入到 Spring 容器中。

Spring XML 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="globalConfig" ref="globalConfig"></property>
</bean>

<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="dbConfig"/>
</bean>

<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
<!-- 注入序列生成器 -->
<property name="keyGenerator" ref="keyGenerator"/>
</bean>

<!-- 定义序列生成器 -->
<bean id="keyGenerator" class="com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator"/>
SpringBoot 配置类
1
2
3
4
5
6
7
8
9
10
@Configuration
@MapperScan("com.clay.mybatis.dao")
public class MybatisPlusConfig {

@Bean
public IKeyGenerator keyGenerator() {
return new OracleKeyGenerator();
}

}

Oracle 多个表共用一个序列

在特殊的业务场景下,若希望多个数据库表共用一个 Sequence 序列,那么可以将 @keySequence 注解定义在父类中,这样就可以实现多个子类对应的多个表共用一个 Sequence 序列。

  • 实体父类
1
2
3
4
@KeySequence(value = "seq_employee", dbType = DbType.ORACLE)
public abstract class BaseEntity {

}
  • 实体子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Employee extends BaseEntity {

@TableId(type = IdType.INPUT)
private Long id;

private String email;

private String lastName;

private String gender;

private Integer age;

....

}

MyBatis-Plus 自定义 ID 生成器

若 MyBatis-Plus 提供的主键生成策略不能满足业务需求,则可以自定义 MyBatis-Plus 的 ID 生成器,例如使用分布式全局唯一 ID 方案来生成主键。

自定义 ID 生成器的步骤

  • 1、实现 IdentifierGenerator 接口
  • 2、将自定义 ID 生成器类注入到 Spring 容器中

自定义 ID 生成器

1
2
3
4
5
6
7
8
9
10
11
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Long nextId(Object entity) {
// 可以将当前传入的class全类名来作为bizKey,或者提取参数来生成bizKey进行分布式ID调用生成
String bizKey = entity.getClass().getName();
// 根据bizKey调用分布式ID生成
long id = ....;
// 返回生成的id值即可
return id;
}
}

注入自定义 ID 生成器

Spring XML 配置文件

1
2
3
4
5
6
7
8
9
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="globalConfig" ref="globalConfig"></property>
</bean>

<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="identifierGenerator" ref="customIdGenerator"/>
</bean>

<bean name="customIdGenerator" class="com.clay.mybatis.incrementer.CustomIdGenerator"/>

SpringBoot 配置类

1
2
3
4
5
6
7
8
9
10
@Configuration
@MapperScan("com.clay.mybatis.dao")
public class MybatisPlusConfig {

@Bean
public IdentifierGenerator idGenerator() {
return new CustomIdGenerator();
}

}

MyBatis-Plus 执行 SQL 分析打印

值得一提的是,执行 SQL 分析打印会产生性能损耗,不建议在生产环境使用。

普通打印方式

使用案例

在 SpringBoot 的 YML 配置文件中,指定 MyBatis-Plus 的 log-impl 配置参数

1
2
3
4
mybatis-plus:
# MyBatis 原生配置
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

打印结果

1
2
3
22:16:13,481 DEBUG getById:137 - ==>  Preparing: select id, last_name as lastName, gender, email from t_employee where id = ?
22:16:13,521 DEBUG getById:137 - ==> Parameters: 1(Long)
22:16:13,570 DEBUG getById:137 - <== Total: 1

可以发现上述的打印方式,不会自动替换 SQL 中的 ? 符号为真实参数值

P6Spy 打印方式

使用案例

引入 Maven 依赖

MyBatis-Plus 执行 SQL 分析打印需要依赖 P6Spy 主组件。

1
2
3
4
5
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
数据库连接配置

提示

  • driver-class-name 为 P6Spy 提供的驱动类
  • url 前缀为 jdbc:p6spy,后面跟着冒号为对应数据库的连接地址
1
2
3
4
5
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/mybatis_plus_lesson?characterEncoding=utf8&autoReconnect=true&useSSL=false&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
...
创建 P6Spy 配置文件

在项目的 src/main/resources 目录下创建 spy.properties 配置文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 3.2.1版本以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 3.2.1版本以下使用或者不配置
# modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
# 日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 SQL
# appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有 error,info,batch,debug,statement,commit,rollback,result,resultset
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
# driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

提示

P6Spy 详细的配置内容请看 这里

日志打印结果

简单执行 CRUD 操作后,MyBatis-Plus 打印的日志信息如下:

1
2
3
4
5
6
7
8
==>  Preparing: SELECT id,email,last_name,gender,age FROM t_employee WHERE id=?
==> Parameters: 1(Long)
Consume Time:2 ms 2022-09-17 19:38:30
Execute SQL:SELECT id,email,last_name,gender,age FROM t_employee WHERE id=1

<== Columns: id, email, last_name, gender, age
<== Row: 1, empty@gmai.com, Clion, 1, 26
<== Total: 1

可以发现 P6Spy 组件会打印出完整的 SQL 语句和执行 SQL 语句所耗费的时间,而且它会自动替换 SQL 中的 ? 为真实参数值并输出。