一、特性
- 无侵入:不影响现有工程
- 损耗小:启动即会注入基本 CURD,性能基本无损耗
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅通过少量配置即可实现大部分单表 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用
- 支持主键自动生成:自由配置 4 种主键策略(内含分布式唯一 ID 生成器-Sequence)
- 支持 ActiveRecord 模式:实体类只需继承 Modal 类即可进行强大的 CRUD 操作
- 内置代码生成器:采用代码或 Maven 插件可快速生成 Mapper、Modal、Service、Controller 层代码,支持模板引擎
- 内置分页插件:基于 MyBatis 屋里分页
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句及其执行时间,快速揪出“慢查询”
- 内置全局拦截插件:提供全表 Delete、Update 操作智能分析阻断,也可自定义拦截规则
二、快速开始
现有一张表 User
,其结构如下:
id | name | age | |
---|---|---|---|
1 | Jone | 18 | test1@baomidou.com |
2 | Jack | 20 | test2@baomidou.com |
3 | Tom | 28 | test3@baomidou.com |
4 | Sandy | 21 | test4@baomidou.com |
5 | Billie | 24 | test5@baomidou.com |
其对应的数据库 Schema 如下:
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
其对应的数据库 Data 脚本如下:
DELETE FROM user;
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
初始化工程
添加依赖
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
添加配置
spring:
datasource:
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
schema: classpath:db/schema.sql
data: classpath:db/data.sql
url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
initialization-mode: always
开启 Mapper 扫描
@SpringBootApplication
@MapperScan("cn.luokaiii.mall.admin.mapper")
public class AdminApplication {
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
编码
// User.java
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
UserMapper.java
中需要加上注解 @Repository
,不然 IDEA 会提示无法注入,但实际可运行。
// UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {
}
测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
List<User> userList = userMapper.selectList(null);
userList.forEach(System.out::println);
}
}
三、注解
@TableName
表名注解
@TableId
主键注解
@TableField
字段注解(非主键)
@Version
乐观锁注解
@EnumValue
通枚举类注解(注解在枚举字段上)
@TableLogic
表字段逻辑处理注解(逻辑删除)
@SqlParser
@KeySequence
序列主键策略(oracle)
四、代码生成器
mybatis-plus-generator
详见 MyBatis-Plus 代码生成器。
五、条件构造器
使用 Wrapper 自定义 SQL,使用方法如下:
// Service.java
mapper.getAll(Wrappers.<User>lambdaQuery().eq(User::getName, "Jack"));
// 注解方式
@Select("select * from user ${ew.customSqlSegment}")
List<User> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);
// XML方式
<select id="getAll" resultType="User">
SELECT * FROM user ${ew.customSqlSegment}
</select>
Wrapper 的条件包含:allEq
、eq
、ne
、gt
、ge
、lt
、le
、between
、notBetween
、like
、notLike
、likeLeft
、likeRight
、isNull
、isNotNull
、in
、notIn
、inSql
、notInSql
、groupBy
、orderByAsc
、orderByDesc
、orderBy
、having
、or
、and
、nested
、apply
、last
、exists
、notExists
QueryWrapper
继承自 AbstractWrapper,可以通过 new QueryWrapper().lambda() 方法获取 LambdaQueryWrapper。
select
// 例:select("id", "name", "age")
select(String... sqlSelect)
// 例:select(i -> i.getProperty().startsWith("test"))
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
UpdateWrapper
继承自 AbstractWrapper,可以通过 new UpdateWrapper().lambda() 方法获取 LambdaUpdateWrapper。
set
// 例:set("name", "zhangsan")
// 设置空值:set("name", "")
// 设置为null:set("name", null)
set(String column, Object val)
set(boolean condition, String column, Object val)
setSql
// 例:setSql("name = 'zhangsan'")
setSql(String sql)
LambdaWrapper
使用 LambdaWrapper,在 QueryWrapper 中是获取 LambdaQueryWrapper;在 UpdateWrapper 中是获取 LambdaUpdateWrapper。
六、分页插件
6.1 配置分页功能
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作,true调回到首页,false继续请求;默认false
paginationInterceptor.setOverflow(false);
// 设置单页限制数量,默认500,-1 不受限制
paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
6.2 使用
// UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {
@Select("select * from user ${ew.customSqlSegment}")
List<User> getAll(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
// SampleTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void test() {
List<User> list = userMapper.getAll(new Page<>(0, 2), Wrappers.<User>lambdaQuery());
list.forEach(System.out::println);
}
}
七、Sequence 主键
Sequence 主键,与 @TableId 的 Input 主键生成策略
配合使用。
内置支持:DB2KeyGenerator、H2KeyGenerator、KingbaseKeyGenerator、OracleKeyGenerator、PostgreKeyGenerator。还可以通过实现 IKeyGenerator 接口来扩展。
7.1 配置
方式一:使用配置类
@Bean
public IKeyGenerator keyGenerator() {
return new H2KeyGenerator();
}
方式二:通过 MybatisPlusPropertiesCustomizer 自定义
@Bean
public MybatisPlusPropertiesCustomizer plusPropertiesCustomizer() {
return plusProperties -> plusProperties
.getGlobalConfig()
.getDbConfig()
.setKeyGenerator(new H2KeyGenerator());
}
八、自定义 ID 生成器
从 3.3.0 开始,默认使用雪花算法+UUID(不含中划线)
8.1 配置
@Bean
public IdentifierGenerator idGenerator() {
return new CustomIdGenerator();
}
九、插件扩展
9.1 逻辑删除
逻辑删除,使用 MyBatis-Plus 自带的方法都会附带逻辑删除功能。想查找到,可以手写 XML。
// application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
// 注解表示为逻辑删除字段,支持 Integer、Boolean、LocalDateTime(null/now())
@TableLogic
private Boolean deleted;
注:逻辑删除与删除等价,不应该再被查询出来。如果需要在查询时使用,可以用状态表示。
9.2 通用枚举
声明枚举属性:
// 使用 @EnumValue 注解枚举属性
public enum GradeEnum {
PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue//标记数据库存的值是code
private final int code;
private final String descp;
}
// 实现 IEnum 接口
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private int value;
private String desc;
@Override
public Integer getValue() {
return this.value;
}
}
使用枚举类型:
public class User {
/**
* 名字
* 数据库字段: name varchar(20)
*/
private String name;
/**
* 年龄,IEnum接口的枚举处理
* 数据库字段:age INT(3)
*/
private AgeEnum age;
/**
* 年级,原生枚举(带{@link com.baomidou.mybatisplus.annotation.EnumValue}):
* 数据库字段:grade INT(2)
*/
private GradeEnum grade;
}
配置扫描通用枚举:
// application.yml
mybatis-plus:
# 支持统配符 * 或者 ; 分割
typeEnumsPackage: com.baomidou.springboot.entity.enums
9.3 字段类型处理器
字段类型处理器,用于 JavaType 与 JdbcType 之间的转换,用于 PreparedStatement 设置参数值和从 ResultSet 或 CallableStatement 中取出一个值,并通过 TableField 注解快速注入。
@Data
@Accessors(chain = true)
// 注意,必须开启映射注解
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 类型处理器,二选一
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
9.4 自动填充功能
字段必须声明 TableField
注解,由属性 fill
选择对应策略。
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
9.5 执行 SQL 分析打印
该插件有性能损耗,不建议生产环境使用。
<!-- pom.xml 引入依赖 -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>版本号</version>
</dependency>
修改 application.yml
文件,修改 jdbc url 和驱动程序类名:
spring:
datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
# url: jdbc:mysql://localhost:3306/mp
url: jdbc:p6spy:mysql://localhost:3306/mp
...
该文件可以从官网下载,并稍作修改:
<!-- spy.properties -->
#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
9.6 乐观锁插件
1.插件配置:
// MybatisPlusConfig.java
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
2.注解实体字段:
// User.java
public class User {
...
@Version
private Integer version;
}
3.测试:需要带版本号更新,否则不会触发乐观锁
// SampleTest.java
@Test
public void test5() {
System.out.println("第一次查询:" + userMapper.selectById(1L));
User user = new User();
user.setId(1L);
user.setName("张三");
user.setVersion(0);
int i = userMapper.updateById(user);
System.out.println("更新后:" + userMapper.selectById(1L));
}
9.7 数据安全保护
1.生成随机 AES 密钥,妥善保管密钥,密钥用来解密 YML 中的加密配置:
@Test
public void test() {
String randomKey = AES.generateRandomKey();
String url = AES.encrypt("jdbc:p6spy:mysql://localhost:3306/mp", randomKey);
String username = AES.encrypt("root", randomKey);
String pwd = AES.encrypt("password", randomKey);
System.out.println("随机秘钥为:" + randomKey);
System.out.println("数据库连接地址为:" + url);
System.out.println("数据库账号为:" + username);
System.out.println("数据库密码为:" + pwd);
}
2.替换 YML 配置,以 mpw 开头,紧接加密内容,YML 中的其它配置也可以使用:
// application.yml
spring:
datasource:
url: mpw:gon0cDU60gipEpMICPWgUJtGmWxnY1riLGNa7MgmN0GG6rl+8yZYh/LyrdSKyOkBDYgOXWuT6m39aghmRweM0wgzhlbYR08m5iJn/fa+GBv3JKxEy8uLHD4dGoQ3mB7TB1Plr/APTsZD6GIR/RakDme/EaMS5AEnMpFz3EYIV3o=
username: mpw:9L9cgOvm5jezPKYgI3DqtA==
password: mpw:fParb8j1C9LPKrGSB6gL3w==
3.项目增加启动参数:
// Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
--mpw.key=d1104d7c3b616f0b
9.8 多数据源
dynamic-datasource-spring-boot-starter
是一个基于 springboot 的快速集成多数据源的启动器。
特性
- 数据源分组,适用于多种场景:纯粹多库;读写分离;一主多从;混合模式。
- 内置敏感参数加密和启动初始化
表结构schema
、数据库database
。 - 提供对 Druid、Mybatis-Plus、P6sy、Jndi 的快速集成。
- 简化 Druid 和 HikariCp 配置,提供全局参数配置。
- 提供自定义数据源来源接口(默认使用 yml 或 properties 配置)。
- 提供项目启动后增减数据源方案。
- 提供 Mybatis 环境下的
纯读写分离
方案。 - 使用 spel 动态参数解析数据源,如从 session,header 或参数中获取数据源。(多租户架构神器)
- 提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个 Service 都是不同的数据源)
- 提供 不使用注解 而 使用 正则 或 spel 来切换数据源方案(实验性功能)。
- 基于 seata 的分布式事务支持。
使用方法
1.引入依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
2.配置数据源:
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_1:
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave_2:
url: ENC(xxxxx) # 内置加密,使用请查看详细文档
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.jdbc.Driver
schema: db/schema.sql # 配置则生效,自动初始化表结构
data: db/data.sql # 配置则生效,自动初始化数据
continue-on-error: true # 默认true,初始化失败是否继续
separator: ";" # sql默认分号分隔符
# 多主多从 纯粹多库(记得设置primary) 混合配置
spring: spring: spring:
datasource: datasource: datasource:
dynamic: dynamic: dynamic:
datasource: datasource: datasource:
master_1: mysql: master:
master_2: oracle: slave_1:
slave_1: sqlserver: slave_2:
slave_2: postgresql: oracle_1:
slave_3: h2: oracle_2:
3.使用@DS切换数据源:
注:1.没有@Ds注解,使用默认数据源(master);2.方法注解优于类注解
@Service
@DS("slave")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Map<String, Object>> selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List<Map<String, Object>> selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
9.9 多租户SQL解析器
多租户技术或称多重租赁技术,是一种软件架构技术,是实现如何在多用户环境下(多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。
10. 踩坑
- MyBatis-Plus 枚举类型无法映射
- 将字段类型从 tinyint(1) 改为 tinyint(4)