目录
- [速通 MyBatis-Plus , 深入浅出 实战演示](#速通 MyBatis-Plus , 深入浅出 实战演示)
-
- 1、简单介绍
- 2、项目演示(CRUD操作及其扩展延伸的操作)
-
- [2-1:简单通过 MyBatis-Plus 查询单表所有数据](#2-1:简单通过 MyBatis-Plus 查询单表所有数据)
-
- 2-1-1:创建项目
- 2-1-2:添加依赖
- [2-1-3:Navicat 添加数据库并初始化数据](#2-1-3:Navicat 添加数据库并初始化数据)
- [2-1-4:简单创建一个 User 实体类](#2-1-4:简单创建一个 User 实体类)
- [2-1-5:创建 Mapper 接口(相当dao接口)](#2-1-5:创建 Mapper 接口(相当dao接口))
- 2-1-6:配置文件添加连接数据库的配置
- [2-1-7:IDEA 添加 MySQL](#2-1-7:IDEA 添加 MySQL)
- [2-1-8:启动类添加 @MapperScan 注解扫描 mapper](#2-1-8:启动类添加 @MapperScan 注解扫描 mapper)
- 2-1-9:简单写个测试,成功查询表中所有数据
- 2-2:配置日志--查看SQL具体执行命令
- 2-3:插入一条数据--->主键生成策略
-
- [2-3-1:MP 默认自动生成id ---> 雪花算法](#2-3-1:MP 默认自动生成id ---> 雪花算法)
- [2-3-2:自增id ---> @TableId(type = IdType.AUTO)](#2-3-2:自增id ---> @TableId(type = IdType.AUTO))
- 2-4:更新操作
-
- [2-4-1:简单更新操作 --->MP会通过条件自动拼接SQL](#2-4-1:简单更新操作 --->MP会通过条件自动拼接SQL)
- [2-4-2:自动填充 ---> 【自定义处理类】实现【创建时间、修改时间】的字段的数据自动填充](#2-4-2:自动填充 ---> 【自定义处理类】实现【创建时间、修改时间】的字段的数据自动填充)
-
- [2-4-2-1:数据库增加 创建时间、修改时间 列](#2-4-2-1:数据库增加 创建时间、修改时间 列)
- [2-4-2-2:同步 User 实体类的字段 ---> @TableField(fill = FieldFill.INSERT)](#2-4-2-2:同步 User 实体类的字段 ---> @TableField(fill = FieldFill.INSERT))
- 2-4-2-3:创建一个处理器类,实现自动填充的功能
- 2-4-2-4:测试插入和更新
- [2-5:使用 MyBatis-plus 的乐观锁插件进行演示](#2-5:使用 MyBatis-plus 的乐观锁插件进行演示)
-
- 2-5-1:官网乐观锁插件代码
- [2-5-1:给数据库中的 User 表增加 Version 字段](#2-5-1:给数据库中的 User 表增加 Version 字段)
- 2-5-2:实体类添加对应的字段
- [2-5-3:注册 乐观锁 组件 --> 添加配置类 (@Bean对象)](#2-5-3:注册 乐观锁 组件 --> 添加配置类 (@Bean对象))
- 2-5-4:测试类测试乐观锁
- 2-5-5:简化乐观锁插件的代码
- 2-6:查询操作
-
- 2-6-1:简单的根据id查询数据
- 2-6-2:测试批量查询
- [2-6-3:按条件查询之一:使用 Map 操作](#2-6-3:按条件查询之一:使用 Map 操作)
- [2-7:分页查询 --->使用MP的分页插件](#2-7:分页查询 --->使用MP的分页插件)
- 2-8:物理删除操作
-
- [2-8-1:根据 id 删除单条数据](#2-8-1:根据 id 删除单条数据)
- [2-8-1:根据 id 批量 删除数据](#2-8-1:根据 id 批量 删除数据)
- [2-8-1:根据 map 按条件删除](#2-8-1:根据 map 按条件删除)
- 2-9:逻辑删除操作
-
- [2-9-1:User表 添加一个 deleted 字段](#2-9-1:User表 添加一个 deleted 字段)
- [2-9-2:实体类 添加一个 deleted 字段(只需添加 @TableLogic 注解即可实现逻辑删除功能)](#2-9-2:实体类 添加一个 deleted 字段(只需添加 @TableLogic 注解即可实现逻辑删除功能))
- [2-9-3:配置文件 添加 逻辑删除配置(现在不用添加该配置也可以,仅用于记录)](#2-9-3:配置文件 添加 逻辑删除配置(现在不用添加该配置也可以,仅用于记录))
- 2-9-4:测试逻辑删除
- 2-9-5:测试查询已经被逻辑删除的数据
- 3、SQL性能分析
-
- 3-1:性能分析插件
-
- 3-1-1:配置类添加性能分析的插件(@Bean对象)
- [3-1-1:配置文件中配置当前项目开发环境为 dev](#3-1-1:配置文件中配置当前项目开发环境为 dev)
- [4、条件查询器 Wrapper(实现复杂查询)](#4、条件查询器 Wrapper(实现复杂查询))
-
- 条件查询
-
- [4-1:查询 name、Email 不为空,age >= 20 的用户【 isNotNull、ge 】](#4-1:查询 name、Email 不为空,age >= 20 的用户【 isNotNull、ge 】)
- [4-2:查询名字等于 xxx 的一条数据【 eq、 selectOne 】](#4-2:查询名字等于 xxx 的一条数据【 eq、 selectOne 】)
- [4-3:查询名字等于 xxx 的数据,返回多条数据【 eq、 selectList 】](#4-3:查询名字等于 xxx 的数据,返回多条数据【 eq、 selectList 】)
- [4-4:查询年龄在 20 - 30 之间的用户 【 between 】](#4-4:查询年龄在 20 - 30 之间的用户 【 between 】)
- [4-5:查询年龄在 20 - 30 之间的用户有几个 【 between 、selectCount 】](#4-5:查询年龄在 20 - 30 之间的用户有几个 【 between 、selectCount 】)
- 模糊查询
-
- [4-6:模糊查询【 like、notLike、likeRight 】](#4-6:模糊查询【 like、notLike、likeRight 】)
- 其他查询
-
- [4-7:子查询 【 inSql 】](#4-7:子查询 【 inSql 】)
- [4-8:排序---根据 id 倒序排序](#4-8:排序---根据 id 倒序排序)
速通 MyBatis-Plus , 深入浅出 实战演示
1、简单介绍
MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
2、项目演示(CRUD操作及其扩展延伸的操作)
2-1:简单通过 MyBatis-Plus 查询单表所有数据
2-1-1:创建项目
我这边先创建一个 springboot 项目,按个人爱好取名。
2-1-2:添加依赖
目前就先添加 mybatis-plus插件依赖、mysql数据库驱动依赖 和 lombok 依赖。
官网的依赖如图:
java
<!-- mybatis-plus插件依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!-- mysql数据库驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2-1-3:Navicat 添加数据库并初始化数据
这里的创建基础数据的 SQL 可以直接从官网的 快速开始 那里拷贝。
sql
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
id BIGINT NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
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');
2-1-4:简单创建一个 User 实体类
java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
2-1-5:创建 Mapper 接口(相当dao接口)
如图,只需简单继承 MyBatis-Plus 的 BaseMapper 接口即可,暂时无需自己写查询方法。
java
/**
* 继承 mybatisplus 的 BaseMapper 接口,就可以用到 mybatisplus 的功能了。
* BaseMapper<User> 的 User 表示这个 UserMapper 接口操作的是 User 这个实体类
*/
//@Repository 也可以用这个注解代表这个接口是持久层
@Mapper
public interface UserMapper extends BaseMapper<User> {
//不需要像以前一样写各种查询方法了,继承这个BaseMapper,已经帮我们把crud编写完成了
}
2-1-6:配置文件添加连接数据库的配置
java
spring.application.name=mybatis-plus
# 连接数据库
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2-1-7:IDEA 添加 MySQL
2-1-8:启动类添加 @MapperScan 注解扫描 mapper
2-1-9:简单写个测试,成功查询表中所有数据
查询成功
2-2:配置日志--查看SQL具体执行命令
java
# 配置日志-- StdOutImpl 默认的控制台输出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
如图,配置完就可以在控制台查看 SQL 的执行步骤了。
2-3:插入一条数据--->主键生成策略
Mybatis-Plus 这里简单说成 MP
2-3-1:MP 默认自动生成id ---> 雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。
其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
如图,使用 Mybatis-Plus 实现出入一条数据,如果我们不指定 id ,那么MP会自动帮我们回填 id ,id 是通过推特的雪花算法实现的。
2-3-2:自增id ---> @TableId(type = IdType.AUTO)
如图,两个条件都满足后,就能实现id的自动递增:
条件1:ID 字段 添加注解:@TableId(type = IdType.AUTO)
条件2:MySQL 的 ID 列,需要勾选上 【自动递增】的选项。
TableId 的一些源码解释:
INPUT 的话,如果我们不手动给对象设置id,会报错
@TableId(type = IdType.INPUT)
private Long id;
注意:如果数据库的【自动递增】选项有勾选上的话,即使插入的对象没有设置id,那么插入的对象的id也是自动递增的。
如果数据库的【自动递增】选项没有勾选上,插入的对象有没有手动填写id,那么就会直接报错。
2-4:更新操作
2-4-1:简单更新操作 --->MP会通过条件自动拼接SQL
我这里修改了年龄和名字这两个字段的数据,可以看出,当该对象需要修改多个字段数据时,那么 MP 会自动帮我们 动态的拼接SQL
只修改单个字段数据的是这样的,对比一下:
2-4-2:自动填充 ---> 【自定义处理类】实现【创建时间、修改时间】的字段的数据自动填充
更新时自动更新一些固定字段(如:创建时间、修改时间 字段)
2-4-2-1:数据库增加 创建时间、修改时间 列
先给数据库表新增几个固定的字段,如 创建时间、修改时间
2-4-2-2:同步 User 实体类的字段 ---> @TableField(fill = FieldFill.INSERT)
2-4-2-3:创建一个处理器类,实现自动填充的功能
通过实现 MetaObjectHandler 接口,重写 insertFill 和 updateFill 方法,实现插入和修改数据时,时间的自动插入和更新。
java
//作为组件交给spring容器进行管理--自动填充日期时间的处理器
@Component
@Slf4j
public class createAndUpdateHandler implements MetaObjectHandler {
/**
* 插入时的填充策略--根据名字去填充
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("插入数据时自动填充开始.........");
//参数1:要修改的字段名 参数2:要插入的字段值 参数3:要给哪个数据处理
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
/**
* 更新时的填充策略
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("更新数据时自动填充开始.........");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
2-4-2-4:测试插入和更新
插入一条新数据,插入时间和更新时间正确。
修改一条数据,创建时间不变,修改时间变化,代码正确。
2-5:使用 MyBatis-plus 的乐观锁插件进行演示
2-5-1:官网乐观锁插件代码
乐观锁:简单来说就是十分乐观,它总认为每次修改都不会出现问题,无论干什么都不用上锁,只需要在修改值的时候通过 version 值判断一下,该值是否在修改的过程中被其他线程修改过,如果没有,则修改成功;如果被其他线程修改过了,则重新自旋。
2-5-1:给数据库中的 User 表增加 Version 字段
先把version版本号的默认值设置为1
2-5-2:实体类添加对应的字段
2-5-3:注册 乐观锁 组件 --> 添加配置类 (@Bean对象)
把官网的乐观锁插件代码直接拷贝过来即可。
顺便添加个依赖
java
@MapperScan("cn.ljh.mybatisplus.mapper") //扫描功能放在这个配置类即可,不用放在启动类
@EnableTransactionManagement //开启自动管理事务的注解,默认也是开启的
@Configuration //配置类
public class MyBatisPlusConfig{
//注册乐观锁插件---可去官方文档获取代码
//这是一个拦截器,在执行所有操作的时候会进行拦截,然后进行自动化的处理
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
依赖:
java
<!-- 使用 MyBatis-plus 乐观锁插件时,需要添加的依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.5.0</version>
</dependency>
2-5-4:测试类测试乐观锁
测试乐观锁--修改数据成功演示(单线程演示)
这是简单的单线程演示,主要是看 MyBatis-Plus 在执行修改操作时,会自动带上 verison 版本号作条件判断。
测试乐观锁--修改数据失败演示(多线程演示)
可以看出,线程A在执行修改的过程中,线程B 出入进来并先于线程A 完成对 id=1 的数据的修改,因为线程B修改成功,所以该数据的版本号 +1 ,然后线程A再执行修改的时候,发现版本号在修改期间已经发生变化了,所以线程A修改失败,接下来会采用 CAS 自旋锁的方式多次尝试提交。
看下 SQL 的执行日志
2-5-5:简化乐观锁插件的代码
上面的是直接拷贝官网的代码丢进来,因为后面在弄 SQL 性能分析插件的时候,有些依赖出现冲突,所以这里改下看能不能使用。
经过测试类测试,这个简化的代码写法也能实现相同的乐观锁功能(版本号判断)。
java
// 简化下乐观锁插件代码
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
log.info("使用到 MyBatis-Plus 乐观锁插件------------------------------------------------------");
return new OptimisticLockerInterceptor();
}
2-6:查询操作
2-6-1:简单的根据id查询数据
2-6-2:测试批量查询
批量查询,MP 这里是通过 IN 关键字来实现的。
java
//测试批量查询
@Test
void selectBatchIdsTest(){
//批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L));
//遍历循环打印
users.stream().forEach(System.err::println);
}
2-6-3:按条件查询之一:使用 Map 操作
注意:使用 Map 操作来查询,SQL 的查询条件是通过 AND 来拼接的。
具体源码:
2-7:分页查询 --->使用MP的分页插件
2-7-1:官网分页插件代码
先看官网的 分页插件
这个是官网提供的分页插件代码:
java
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加
// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
return interceptor;
}
}
2-7-2:在配置类添加分页插件(@Bean对象)
直接拷贝过来,我这里修改了方法的名称。
2-7-4:测试类测试分页插件功能
2-7-5:简化分页插件的代码
上面的是直接拷贝官网的代码丢进来,因为后面在弄 SQL 性能分析插件的时候,有些依赖出现冲突,所以这里改下看能不能使用。
经过测试类测试,这个简化的代码写法也能实现相同的分页插件功能。
2-8:物理删除操作
2-8-1:根据 id 删除单条数据
2-8-1:根据 id 批量 删除数据
2-8-1:根据 map 按条件删除
注意:通过 Map 按条件删除数据,动态拼接删除条件用的是 AND 。
2-9:逻辑删除操作
物理删除:直接从数据库中删除掉。
逻辑删除:没有删除掉,只是通过修改一个变量让其失效,在查询的时候,带上该变量即可。
2-9-1:User表 添加一个 deleted 字段
2-9-2:实体类 添加一个 deleted 字段(只需添加 @TableLogic 注解即可实现逻辑删除功能)
如图:根据官网提示,给逻辑删除字段添加一个 @TableLogic 注解。
只需要添加 @TableLogic 注解 这个逻辑删除的注解,就可以实现逻辑删除功能了,
经过测试,默认数据未删除时,deleted = 0;
数据已删除时,deleted=1;
2-9-3:配置文件 添加 逻辑删除配置(现在不用添加该配置也可以,仅用于记录)
后面经过测试,即使没有配置这个,也能进行逻辑删除。MP 默认逻辑删除的字段值为 1。
2-9-4:测试逻辑删除
注意:逻辑删除其实就是一个更新deleted字段的更新操作而已。
执行删除,再也不是物理删除了。
该条记录依然存在数据库,只是 deleted 字段的值发生变化而已。
2-9-5:测试查询已经被逻辑删除的数据
如图,数据库表有该条记录,但是没查出来,因为 deleted 字段添加了 @TableLogic 逻辑删除注解后,执行删除时,MP 会自动拼接这个【AND deleted= 0】 的查询条件。
默认 deleted = 0 为【未删除】,deleted = 1为【已删除】
3、SQL性能分析
对于开发中遇到的慢SQL,我们可以使用 MP 提供的性能分析插件来检测,如果该 SQL 执行的时间超过我们设置的时间,就停止运行。
3-1:性能分析插件
作用:SQL 性能分析拦截器,用于输出每条 SQL 语句及其执行时间。
正式开发环境,这个插件就不能部署上去了,只能在开发的时候自己测试使用。
3-1-1:配置类添加性能分析的插件(@Bean对象)
3-1-1:配置文件中配置当前项目开发环境为 dev
注意:因为我这个插件和上面的分页和乐观锁插件的版本起冲突了,先把代码思路写下来,版本冲突后续再解决。
4、条件查询器 Wrapper(实现复杂查询)
条件查询
4-1:查询 name、Email 不为空,age >= 20 的用户【 isNotNull、ge 】
isNotNull:不为空
ge:大于等于
java
//查询 name、Email 不为空,age >= 20 的用户
@Test
void selectListTest(){
//封装查询条件的对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNotNull("name") //name 不为空
.isNotNull("email") //email 不为空
.ge("age",20); //age >= 20
userMapper.selectList(queryWrapper).forEach(System.err::println);
}
4-2:查询名字等于 xxx 的一条数据【 eq、 selectOne 】
selectOne:查询一条数据
注意:如果查询一个数据,可能会出现多条数据的话,需要用 List 或者 Map 接收,否则会报错
java
//查询名字等于 xxx 的一条数据
@Test
void selectOneTest(){
//创建封装查询条件的Wrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//如果查询一个数据,可能会出现多条数据的话,需要用 List 或者 Map 接收,否则会报错
queryWrapper.eq("name","老白");
User user = userMapper.selectOne(queryWrapper);
System.err.println(user);
}
4-3:查询名字等于 xxx 的数据,返回多条数据【 eq、 selectList 】
selectList :查询多条数据
查询名字等于 xxx 的数据,会返回多条数据, List 或者 Map 接收
java
//查询名字等于 xxx 的数据,会返回多条数据, List 或者 Map 接收
@Test
void selectOnesTest(){
//创建封装查询条件的Wrapper对象
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name","老紫");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.err::println);
}
4-4:查询年龄在 20 - 30 之间的用户 【 between 】
between 是闭区间,age = 20 和 age = 30 都会查出来
java
//查询年龄在 20 - 30 之间的用户
@Test
void te(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//是闭区间,age = 20 和 age = 30 都会查出来
queryWrapper.between("age","20","30");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.err::println);
}
4-5:查询年龄在 20 - 30 之间的用户有几个 【 between 、selectCount 】
selectCount:查询符合条件的数据条数。
java
//查询年龄在 20 - 30 之间的用户有几个
@Test
void betweenAndCountTest(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//是闭区间,age = 20 和 age = 30 都会查出来
queryWrapper.between("age","20","30");
Integer UserCount = userMapper.selectCount(queryWrapper);
System.err.println("该区间用户数量:" + UserCount);
}
模糊查询
4-6:模糊查询【 like、notLike、likeRight 】
likeLeft:就是 % 放左边,相当于 %xxx
likeRight:相当于 xxx%
java
// 模糊查询【 like、notLike、likeRight 】
@Test
void test(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// notLike:相当于 not Like "%红%"
// likeRight:相当于 "xxx%",right 就表示 % 放在右边,说明这里是以 xxx 为开头
queryWrapper
.like("name","紫") // %紫%
.notLike("name","白") // %白%
.likeRight("email","123"); // 123%
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.err::println);
}
其他查询
4-7:子查询 【 inSql 】
java
// id 在子查询中查出来
@Test
void inSqlTest(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id","select id from user where id < 8");
List<Object> objs = userMapper.selectObjs(queryWrapper);
objs.forEach(System.err::println);
}
4-8:排序---根据 id 倒序排序
java
// 排序---根据 id 倒序排序
@Test
void orderByDescTest(){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.err::println);
}