一、特性
- 无侵入:不影响现有工程
- 损耗小:启动即会注入基本 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)