MyBatisPlus 进阶篇
衔接入门篇哦
接下来写一些mabatisplus的核心功能和扩展插件
1.配置日志
- 我们所有的sql现在是不可见的,我们希望知道它是怎么执行的,所以我们必须要看日志!
- 配置日志完成之后,后面的学习就要特别注意这个自动生成的SQL
# Mybatis_Plus配置(properties)
## 日志配置
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2.主键生成策略
2.1 默认策略:ID_WORKER 全局唯一ID(使用较多)
- 雪花算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。
2.2 主键自增策略(使用较多)
- 实体类字段上加上 @TableId(type = IdType.AUTO)
- 数据库字段一定要是自增的
- 再次插入可测试自增主键
2.3 IdType的所有策略
public enum IdType {
AUTO(0), // 数据库id自增
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ID_WORKER(3), // 默认的全局唯一id
UUID(4), // 全局唯一id
uuid ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}
3. 自动填充插件
3.1 自动填充简介
- 阿里巴巴手册中明确写道:所有数据表必须包含gmt_creat.gmt_modifiled并且自动化
- 简单来说就是表中每条数据的创建时间和修改时间必须有且自动化完成
- 数据库级别及代码级别,以下仅介绍代码级别(工作中不允许修该数据库)
3.2 自动填充具体操作
- 数据库中创建creat_time,update_time
- 在实体类中加上两个字段并添加注解
//字段添加填充內容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
- 编写处理器处理自动填充注解
package com.kuang.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
时间处理器
自动填充时间处理
在更新和插入时自动修改时间
*/
@Slf4j
@Component //一定不要忘记把处理器加入IOC容器中
public class MyMetaObjrctHandler implements MetaObjectHandler {
//插入时填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("Start insert fill......");
//在执行插入时自动将创建时间与更新加入进去
this.setFieldValByName("createTime",new Date(), metaObject);
this.setFieldValByName("updateTime",new Date(), metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("update insert fill......");
//在执行更新时自动将更新时间插入
this.setFieldValByName("updateTime",new Date(), metaObject);
}
}
- 测试插入 观察创建时间和更新时间即可
4. 乐观锁(老师说很简单,过几天看了JUC再具体写一篇)
意图:当要更新一条记录的时候,希望这条记录没有被别人更新
悲观锁:顾名思义十分悲观,它总是认为会出问题,无论干什么都要上锁!再去操作!
乐观锁:顾名思义十分乐观,它总是认为不会出问题,无论干什么都不去上锁!如果出现了问题,再次更新值测试
4.1 乐观锁实现方式
- 取出记录时,获取当前的version
- 更新时,带上这个version
- 执行更新时,set version = newVersion where version = oldVersion
- 如果version不对就更新失败
4.2 乐观锁:
1、先查询,获得版本号 version = 1
-- A update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
-- B 线程抢先完成,这个时候 version = 2,会导致 A 修改失败!
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
我自己的理解:A 准备更新,但B抢先更新,B更新时已经修改version值为2。
当A再更新时发现version已经不再是1,则无法实现更新。(version一致时才可以更新)
在多线程的情况下一定要加锁
4.3 乐观锁具体操作
- 给数据库加上version字段
- 实体类加上对应字段和注解
//乐观锁version注解
@Version
private Integer version;
- 编写mybatisplusconfig文件,注册乐观锁插件,并将扫描开关从启动类移动到配置类上
//扫描mapper文件夹
@MapperScan("com.kuang.mapper")
@EnableTransactionManagement //自动管理事务 默认开启
@Configuration //配置类注解
public class MyBatisPlusConfig {
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
- 4.4 乐观锁测试
//测试乐观锁成功! 单线程
@Test
void testOptimisticLocker() {
//1.查询用户信息
User user = userMapper.selectById(1);
//2.修改用户
user.setName("lujianbin");
user.setAge(41);
user.setEmail("jianbin.lu@foxmail.com");
//3.执行更新操作
userMapper.updateById(user);
}
//测试乐观锁失败! 多线程情况下一定要加锁
@Test
void testOptimisticLocker2() {
//线程1 进行查询并未更新
User user = userMapper.selectById(1);
user.setName("lujianbin111");
user.setAge(41);
user.setEmail("jianbin.lu@foxmail.com");
//模拟另外一个线程进行了插队操作
User user2 = userMapper.selectById(1);
user2.setName("lujianbin222");
user2.setAge(41);
user2.setEmail("jianbin.lu@foxmail.com");
userMapper.updateById(user2); //抢先更新
//自旋锁进行多次尝试进行提交
//如果没有乐观锁就会覆盖插队线程的值! 乐观锁存在则是插队线程的值
userMapper.updateById(user);
}
```'
## 5. 查询操作
```java
//测试查询
@Test
//查询单个用户
void testSelectById() {
User user = userMapper.selectById(2);
System.out.println(user);
}
//查询多个用户
@Test
void testSelectByBatchId() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println); //第一次知道这样输出
}
//按条件查询之一 使用map操作 (可以使用map也可以使用wrapper条件构造器)
@Test
void testSelectByBatchIds() {
HashMap<String, Object> map = new HashMap<>();
//自定义查询条件
map.put("name", "路建彬");
map.put("age", "22");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
6. 删除操作
6.1 根据id进行删除
//测试删除 通过Id删除
@Test
void testDeleteById()
{
userMapper.deleteById(2L);
}
//通过Id批量删除
@Test
void testDeleteBatchById() {
userMapper.deleteBatchIds(Arrays.asList(1249178144225431555L, 1249178144225431556L));
}
//通过条件删除 = 通过map删除
@Test
void testDeleteMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "lujianbin222");
userMapper.deleteByMap(map);
}
注:
将条件丢到map集合内,wrapper对象用方法去操作
条件查询/删除时可以用map或者wrapper条件构造器
7. 逻辑删除
物理删除:从数据库中直接移除
逻辑删除:在数据库中没有删除,而是通过一个变量来它失效 deleted=o => deleted=1
逻辑删除后deleted值变为1过后,数据库没有删除
7.1 逻辑删除的具体操作
- 在数据库添加deleted字段
- 在实体类中增加属性
//逻辑删除注解
@TableLogic
private int deleted;
- 配置逻辑删除组件
//逻辑删除组件
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
- 添加逻辑删除的配置
## 逻辑删除配置 没有删除为0 删除为1
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
-
测试逻辑删除(实际上走的是更新操作将deleted的值变为1)
-
测试查询进行对比
逻辑删除其实就是增加了deleted字段,实际上是进行update操作更改deleted值
查询时拼接了deleted属性,deleted=0时才是可见的
8. 分页插件
8.1 分页查询简介
- 原始的limit进行分页
- pageHelper第三方插件
- MP其实也内置了插件,实际上是将limit封装成page对象进行操作
8.2 分页查询具体操作
- 配置分页组件
//分页插件(删除默认设置)
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
- 测试直接用Page对象进行操作
//测试分页插件
@Test
void testPage() {
//第一页每页只要5个数据
//当前页,页面大小
//分页插件真香
Page<User> page = new Page<>(2, 5);
userMapper.selectPage(page, null);
//查询记录
page.getRecords().forEach(System.out::println);
//查询总数
System.out.println(page.getTotal());
}
9. 性能分析插件
9.1 性能分析插件的作用
- 性能分析拦截器超过执行时间则停止执行,分析慢sql,用于输出每条sql语句及其执行时间
9.2 性能分析插件的具体操作
- 配置性能分析组件
//性能分析插件
@Bean
@Profile({"dev", "test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
//在工作中不允许用户等待
performanceInterceptor.setMaxTime(100); //设置sql执行的最大时间,如果超过则不执行
//开启格式化支持
performanceInterceptor.setFormat(true);
return new PerformanceInterceptor();
}
- 设置开发环境
#设置开发环境
spring.profiles.active=dev
- 测试使用:超过设置的100ms执行时间就会抛出异常
10. 条件构造器(举几个例子,其它的可以去官方文档看看)
十分重要:wrapper
复杂的sql语句可以用它来代替
10.1 wrapper测试,记住查看输出的SQL进行分析
//查询name不为空并且邮箱不为空且年龄大于12
@Test
void test1() {
//与map进行对比 map是一个集合,可以往里面直接put值。
//wrapper是一个对象, 它有很多的方法 可以链式编程
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age", "21");
userMapper.selectList(wrapper).forEach(System.out::println);
}
//查询名字为路建彬的用户 可以用map也可以用wrapper
@Test
void test2() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.eq("age", "21");
userMapper.selectList(wrapper).forEach(System.out::println);
}
//查询年龄在20-23的用户
@Test
void test3() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.between("age", "20", "23"); //区间
Integer count = userMapper.selectCount(wrapper);
System.out.println(count);
}
//模糊查询
//名字不含e 且以j开头的邮箱
@Test
void test4() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("name", "e")
.likeRight("email", "j"); //右 j%
userMapper.selectList(wrapper).forEach(System.out::println);
// 也可以用map输出
//userMapper.selectMaps(wrapper).forEach(System.out::println);
}
//测试排序 通过Id降序排序
@Test
void test5() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.orderByDesc("id");
userMapper.selectList(wrapper).forEach(System.out::println);
}
//通过wrapper进行删除
@Test
void test6() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.eq("age", "22");
userMapper.delete(wrapper);
}
已上为mybatisplus的核心功能和插件,没有写完官网还有很多大家可以自行探索