MyBatis-Plus

简介

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

基于SpringBoot使用MyBatisPlus

数据库设计

image-20220704195430412

新建项目

image-20220703221600684

image-20220703221718341

image-20220703221735710

image-20220703221754550

导入依赖

image-20220704194517877

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
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

编辑配置

image-20220704194831358

1
2
3
4
5
6
7
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
username: root
password: 13851176590++

添加启动类

image-20220704201230606

1
2
3
4
5
6
@SpringBootApplication
public class MyBatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisPlusApplication.class, args);
}
}

创建实体类

image-20220704195648242

1
2
3
4
5
6
7
8
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}

创建Dao接口

image-20220704195842852

1
2
3
4
@Mapper
public interface UserMapper extends BaseMapper<User> {

}

创建测试类

image-20220704201317232

1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootTest
public class MyBatisPlusApplicationTest {
@Resource
private UserMapper userMapper;
private static final Logger logger = LoggerFactory.getLogger(MyBatisPlusApplicationTest.class);
// 查询列表
@Test
void getAll(){
List<User> users = userMapper.selectList(null);
logger.info("用户列表:" + users);
}
}

简化日志输出

新建logback.xml配置文件

image-20220704210131509

编辑配置

image-20220704210209383

1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>

关闭Banner

image-20220704210523918

启动测试

image-20220704201439508

标准数据层开发

image-20220704202159031

新增操作

image-20220704202540481

1
2
3
4
5
@Test
void saveUser() {
User user = new User().setName("xiaohong").setAge(21).setPassword("123456").setTel("123456");
Integer count = userMapper.insert(user);
}

image-20220704202602872

删除操作

image-20220704202734472

image-20220704202937068

修改操作

image-20220704203046171

1
2
3
4
5
@Test
void updateUser() {
User user = new User().setId(1L).setAge(50);
userMapper.updateById(user);
}

image-20220704203202535

分页查询

配置分页拦截器

image-20220704204825107

编辑测试类

image-20220704203919463

image-20220704204928225

添加日志输出

image-20220704205216716

image-20220704205339781

DQL

条件查询

查看Wrapper抽象类

image-20220704210847732

image-20220704213548048

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
    // 条件查询
// 方法一:常规格式
@Test
void getByQueryWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
queryWrapper.gt("age", 20).lt("age", 40);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(user -> logger.info("用户:" + user));
}
// 方法二:lambda格式
@Test
void getByQueryWrapperForLambda() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
queryWrapper.lambda().gt(User::getAge, 20).lt(User::getAge, 40);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(user -> logger.info("用户:" + user));
}
// 方法三:lambda
@Test
void getByLambdaQueryWrapper() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
// 条件:age > 20 and age < 40
lambdaQueryWrapper.gt(User::getAge, 20).lt(User::getAge, 40);
// 条件: age < 20 or age > 40
// lambdaQueryWrapper.gt(User::getAge, 40).or().lt(User::getAge, 20);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(user -> logger.info("用户:" + user));
}

image-20220704213630003

空值判定

新建用户查询条件类

image-20220704214625224

空值测试

image-20220704215213584

image-20220704215230612

空值判定方法

方法一:if条件语句控制

image-20220704220102139

1
2
3
4
5
6
7
8
9
10
11
12
13
14
![image-20220704220336783](C:\Users\YuanJW\AppData\Roaming\Typora\typora-user-images\image-20220704220336783.png)    @Test
void getByIfNull() {
UserQuery userQuery = new UserQuery();
userQuery.setLowerAge(20).setUpperAge(40);
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
if (userQuery.getLowerAge() != null) {
lambdaQueryWrapper.gt(User::getAge, userQuery.getLowerAge());
}
if (userQuery.getUpperAge()![image-20220704220336783](C:\Users\YuanJW\AppData\Roaming\Typora\typora-user-images\image-20220704220336783.png) != null) {
lambdaQueryWrapper.lt(User::getAge, userQuery.getUpperAge());
}
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(user -> logger.info("用户:" + user));
}
方法二:条件参数控制

image-20220704220338824

1
2
3
4
5
6
7
8
9
10
@Test
void getByCondition() {
UserQuery userQuery = new UserQuery();
userQuery.setLowerAge(20).setUpperAge(40);
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
lambdaQueryWrapper.gt(userQuery.getLowerAge() !=null, User::getAge, userQuery.getLowerAge())
.lt(userQuery.getUpperAge() != null, User::getAge, userQuery.getUpperAge());
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(user -> logger.info("用户:" + user));
}

查询投影

情况一:LambdaQueryWrapper

image-20220704223005342

情况二:QueryWrapper

image-20220704223342994

测试

image-20220704223443857

image-20220704224013893

1
2
3
4
5
6
7
8
9
10
11
// 统计映射
@Test
void getTotal(){
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
// 统计
queryWrapper.select("count(*) as total", "age");
// 分组
queryWrapper.groupBy("age");
List<Map<String, Object>> map = userMapper.selectMaps(queryWrapper);
System.out.println(map);
}

image-20220704224108616

查询条件设置

API查询地址:https://baomidou.com/pages/10c804/#nested

等值查询

image-20220705143514861

1
2
3
4
5
6
7
8
9
// 登录验证
@Test
void login() {
// 等值设置
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
lambdaQueryWrapper.eq(User::getName, "xiaomi").eq(User::getPassword, "123456");
User user = userMapper.selectOne(lambdaQueryWrapper);
System.out.println(user);
}

image-20220705143605575

范围查询

image-20220705144336093

1
2
3
4
5
6
7
8
9
10
// 范围查询
@Test
void between() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
lambdaQueryWrapper.between(User::getAge, 20, 40);
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(user -> {
System.out.println(user);
});
}

image-20220705144401989

模糊匹配

image-20220705150836409

1
2
3
4
5
6
7
8
// 模糊匹配
@Test
void like() {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
lambdaQueryWrapper.like(User::getName, "xiao");
List<User> users = userMapper.selectList(lambdaQueryWrapper);
users.forEach(System.out::println);
}

image-20220705150855577

映射匹配

数据库

image-20220705161614345

实体类映射

image-20220705153154091

测试

image-20220705153059547

DML

Insert

id生成策略控制

场景分析

image-20220705153402857

image-20220705162607843

@TableId()注释

image-20220705162205585

测试

image-20220705162417709

全局配置

image-20220705163327730

1
2
3
4
5
6
7
8
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # MybatisPlus日志输出
global-config:
banner: false # 关闭MybatisPlus的banner
db-config:
id-type: assign_id # id生成算法全局配置
table-prefix: ums_ # 表名前缀

image-20220705163517590

Delete

批量删除

image-20220705164213057

1
2
3
4
5
6
7
@Test
void batchDelete() {
// 数组转List
List<Long> ids = Arrays.asList(new Long[]{1544234982638149633L, 1544235553495506945L});
// 批量删除
Integer count = userMapper.deleteBatchIds(ids);
}

image-20220705164313573

逻辑删除

数据库添加逻辑删除字段

image-20220705164959329

实体类添加字段并设定逻辑删除标记字段

image-20220705165955206

测试

image-20220705165752771

1
2
3
4
5
// 逻辑删除
@Test
void deleteByLogic() {
Integer count = userMapper.deleteById(1L);
}

image-20220705170207379

image-20220705170231137

image-20220705170936129

image-20220705170909635

全局配置

image-20220705170536943

Update

乐观锁

当更新一条记录时,希望这条记录没有被别人更新

乐观锁的实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果version不对,更新失败
数据库添加字段

image-20220705173918160

实体类添加字段

image-20220705174101440

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(chain = true)/*对象链式赋值*/
@EqualsAndHashCode(callSuper = false)
//@TableName("ums_user")/*表名映射*/
public class User {
// @TableId(type = IdType.ASSIGN_ID)/*id生成策略*/
private Long id;
private String name;
@TableField(value = "pwd", select = false)/*表名映射*/
private String password;
private Integer age;
private String tel;
@TableField(exist = false)
private Integer online;
@TableField(value = "is_delete")
@TableLogic(value = "0", delval = "1")
private Integer deleted;
@Version
private Integer version;/*乐观锁Version*/
}
添加配置

image-20220705174204504

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* MyBatisPlus配置类
* Created by YuanJW on 2022/7/4.
*/
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 定义MyBatisPlus拦截器
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 添加分页拦截器
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 添加乐观锁拦截器
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}

测试

image-20220705175322928

1
2
3
4
5
6
7
// 乐观锁
@Test
void update() {
User user = new User().setId(3L).setName("aa")
.setVersion(1); // setVersion()开启锁机制
Integer count = userMapper.updateById(user);
}

image-20220705175127188

并发测试

image-20220705180306328

1
2
3
4
5
6
7
8
9
10
// 并发测试
@Test
void updateByConcurrency() {
User user1 = userMapper.selectById(3L);
User user2 = userMapper.selectById(3L);
user1.setName("aa");
Integer count1 = userMapper.updateById(user1);
user2.setName("bb");
Integer count2 = userMapper.updateById(user2);
}

image-20220705180403752

image-20220705180423160

代码生成器

导入依赖

image-20220705211930870

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
<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- MP代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
</dependencies>

添加代码自动生成器类

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
/**
* 代码自动生成器
* Created by YuanJW on 2022/7/5.
*/
public class Generator {
public static void main(String[] args) {
// 代码生成器对象
AutoGenerator autoGenerator = new AutoGenerator();
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("13851176590++");
autoGenerator.setDataSource(dataSourceConfig);
// 设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir("D:\\Users\\YuanJW\\Desktop\\Java Learn\\MyBatis-Plus\\MyBatisPlusGenerator\\src\\main\\java") //文件输出位置
.setOpen(false)
.setAuthor("xiaoyuanjw")
.setFileOverride(true)
.setMapperName("%sMapper")
.setIdType(IdType.ASSIGN_ID);
autoGenerator.setGlobalConfig(globalConfig);
// 设置包名相关配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.demo")
.setEntity("domain")
.setMapper("dao");
autoGenerator.setPackageInfo(packageConfig);
// 策略设置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("ums_user")
.setTablePrefix("ums_")
.setRestControllerStyle(true)
.setVersionFieldName("version")
.setLogicDeleteFieldName("deleted")
.setEntityLombokModel(true);
autoGenerator.setStrategy(strategyConfig);
// 执行代码生成器
autoGenerator.execute();
}
}

生成结构

image-20220705214654400