SpringBoot整合mybatis-plus--进阶

前言

关于mybatis-plus的简介以及基本使用,我在《SpringBoot整合mybatis-plus–入门超详细》一文中已做介绍,此处不再赘述。本文主要对mybatis-plus的条件构造器、AR模式、插件、逆向工程、自定义全局操作、公共字段自动填充等知识点进行讲解。

条件构造器 AbstractWrapper

wapper介绍 :

List item

  • Wrapper:条件构造抽象类,最顶端父类,抽象类中提供4个方法西面贴源码展示
  • AbstractWrapper:用于查询条件封装,生成 sql 的 where 条件
  • AbstractLambdaWrapper:Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
  • LambdaQueryWrapper:看名称也能明白就是用于Lambda语法使用的查询Wrapper
  • LambdaUpdateWrapper:Lambda 更新封装Wrapper
    QueryWrapper:Entity 对象封装操作类,不是用lambda语法
    UpdateWrapper:Update 条件封装,用于Entity对象更新操作

上一节我们完成了基于mybatis-plus的CRUD操作,这一节我们来学习一下使用mybatis-plus中的条件构造器——AbstractWrapper。

一、什么是AbstractWrapper

QueryWrapper(LambdaQueryWrapper)UpdateWrapper(LambdaUpdateWrapper)的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为

首先我们来介绍一下AbstractWrapper,下图是AbstractWrapper的一个继承结构:
在这里插入图片描述

二、QueryWrapper(LambdaQueryWrapper)

由于其中方法太多就不一一赘述,想看全部方法用法可以看官网介绍或者看我的实例代码

1、QueryWrapper用法示例

查询id在1 - 30且性别为男、姓名带有test的用户:

@SpringBootTest
@SuppressWarnings("unchecked")
public class UserWrapperTest {

    @Autowired
    UserMapper userMapper;
    @Test
    public void TestQueryWrapper(){
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.like("name","test");
        userQueryWrapper.eq("sex","男");
        userQueryWrapper.between("id",1,30);
        userMapper.selectList(userQueryWrapper).forEach(System.out::println);
    }
}

:eq是equals的简写,该方法两个参数,column和value,表示column的值和value要相等。
此处like方法就是查询name中包含“test”字样的记录;between方法三个参数,分别是column、value1、value2,该方法表示column的值要在value1和value2之间。
注意column是数据表对应的字段,而非实体类属性字段。

运行日志
在这里插入图片描述

2、LambdaQueryWrapper用法示例

LambdaQueryWrapper是mybatis plus中的一个条件构造器对象,只是是需要使用Lambda 语法使用 Wrapper

    @Test
    public void TestLambdaQueryWrapper() {
        LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
        lambdaWrapper.like(User::getName,"test");
        lambdaWrapper.eq(User::getSex,"男");
        lambdaWrapper.between(User::getId,1,30);
        userMapper.selectList(lambdaWrapper).forEach(System.out::println);
    }

运行日志
在这里插入图片描述

三、UpdateWrapper(LambdaUpdateWrapper)

我们再进行更新或者删除的时候,有时候where条件复杂的话,可以使用UpdateWrapper来构造条件
他们有部分方法是和上面的QueryWrapper(LambdaQueryWrapper) 相同的。这里演示不同之处

1、UpdateWrapper用法示例

将名字包含test1的用户名改为test1.1 :

    @Test
    public void TestUpdateWrapper() {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        //继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
        //及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!
        User user = User.builder().build();
        //修改语句
        updateWrapper.set("name", "test1.1");
        //条件
        updateWrapper.like("name", "test1");
        userMapper.update(user, updateWrapper);
    }

运行日志
在这里插入图片描述

2、LambdaUpdateWrapper用法示例

    @Test
    public void TestLambdaUpdateWrapper() {
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        //LambdaUpdateWrapper<User> updateWrapper = new UpdateWrapper<User>().lambda();
        User user = User.builder().build();
        //修改语句
        updateWrapper.set(User::getName, "test1.1");
        //条件
        updateWrapper.like(User::getName, "test1");
        userMapper.update(user, updateWrapper);
    }

在这里插入图片描述


AR模式 – Active Record

Active Record(活动记录),是一种领域模型模式,特点是一个模型类对应关系型数据库中的一个表,而模型类的一个实例对应表中的一行记录。ActiveRecord 一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,对于 ActiveRecord 往往只能感叹其优雅,所以 MP 也在 AR 道路上进行了一定的探索,仅仅需要让实体类继承 Model 类且实现主键指定方法,即可开启 AR 之旅。接下来看具体代码:

一、entity:

@EqualsAndHashCode(callSuper = true)
@Data
@Builder
@TableName(value = "User")//指定表名
public class User extends Model<User> {
    private static final long serialVersionUID = -7585862229833387698L;
    //value与数据库主键列名一致,若实体类属性名与表主键列名一致可省略value
    @TableId(value = "id", type = IdType.AUTO)//指定自增策略
    private Integer id;
    private String name;
    private String sex;
    private String pwd;
    private String email;
    @Version
    private Long version;
    
    public void println(){
        System.out.println(this);
    }
    
    @Override
    public Serializable pkVal() {
        return id;
    }

}

注: 实体类继承Model类,重写pkVal方法。

二、mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

注:虽然AR模式用不到该接口,但是一定要定义,否则使用AR时会报空指针异常。

三、使用AR:

1、insert操作:

    @Test
    public void testInsert(){
        User user = User.builder().name("张三").sex("男").pwd("bbb").email("111@qq.com").build();
        boolean b = user.insert();
        System.out.println(b);
       
    }

运行结果
在这里插入图片描述
注: 可以看到我们并不需要注入mapper接口,不过正如刚才所说,不使用但还是要定义,否则会报错。AR操作是通过对象本身调用相关方法,比如要insert一个user,那就用这个user调用insert方法即可。返回值为布尔类型,由上图可看到返回了true,是操作成功的。

2、select操作

 @Test
    public void testSelect() {
        User user = User.builder().build();
        //查询全部
        user.selectAll().forEach(System.out::println);
        //通过id查询
        user.selectById(65).println();
        //或者这样写
        user.setId(65);
        user.selectById();
        //分页查询
        IPage<User> page = user.selectPage(new Page<User>(2, 10), new QueryWrapper<User>().like("name", "test"));
        page.getRecords().forEach(System.out::println);
        //查询总数
        user.selectCount(null);
        // 条件查询总记录数
        user.selectList(new QueryWrapper<>());
        //查询一条记录
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",2);
        user.selectOne(queryWrapper);
    }

注: 上面的代码涉及到了几种不同的查询操作,其实用法与MP的BaseMapper提供的方法的用法差不多,只不过这里是实体对象调用。
其中分页 是需要配置分页插件才能实现 至于如何配置下文有详细介绍

3、update操作

    @Test
    public void testUpdate() {
        User user = User.builder().build();
        //获取id为2的用户信息
        user = user.selectById(2);
        user.setName("李四");
        //通过id修改用户
        user.updateById();
        //条件修改
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        //修改语句
        updateWrapper.set("name", "李四");
        //条件
        updateWrapper.like("name", "刘亦菲");
        user.update(updateWrapper);
    }
    @Test
    public void insertOrUpdate(){
        User user = User.builder().id(50).name("张三").sex("男").pwd("bbb").email("111@qq.com").build();
        user.insertOrUpdate();
    }

注: 这个insertOrUpdate是通过判断id是否存在,来实现的。

4、delete操作

    @Test
    public void testDelete() {
        User user = User.builder().build();
        //通过id删除
        user.deleteById(59);
        //或者这样写
        user.setId(60);
        user.deleteById();
        //条件删除
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", 61);
        user.delete(queryWrapper);
    }

注: 这里介绍了两个删除方法,代码中已有注释说明。需要注意的是,删除数据库中不存在的数据,结果也是true。

mybatis-plus的插件

mybatis-plus提供了很多好用的插件,而且配置简单,使用方便。接下来一起看看MP的插件如何使用。

一、分页插件

BaseMapper的selectPage方法和AR提供的selectPage方法都不是物理分页,需要配置分页插件后才是物理分页,那么现在就来看看如何配置这个插件。分页查询

1、配置分页插件

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: springboot
 * @description:
 * @author: King
 * @create: 2021-09-08 13:18
 */
@Configuration
public class MybatisPlusConfig {
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题
     * 3.4.3不用设置
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
}

这个配置摘抄自官方示例

2、测试案例

    @Autowired
    UserMapper userMapper;
    
   @Test
    public void Test2() {
        //配置了分页插件后,使用selectpage方法,
        //但是现在就是真正的物理分页了,sql语句中有limit了
        Page<User> page = new Page<>(1, 10);
        IPage<User> selectPage = userMapper.selectPage(page, null);
        System.out.println(selectPage);
        System.out.println("================= 相关的分页信息 ==================");
        System.out.println("总条数:" + selectPage.getTotal());
        System.out.println("当前页码:" + selectPage.getCurrent());
        System.out.println("总页数:" + selectPage.getPages());
        System.out.println("每页显示条数:" + selectPage.getSize());
        System.out.println("是否有上一页:" + page.hasPrevious());
        System.out.println("是否有下一页:" + page.hasNext());
        System.out.println("查询结果:");
        List<User> list = selectPage.getRecords();
        list.forEach(o -> System.out.println(o));
//        page.getRecords().forEach(o -> System.out.println(o));
        //还可以将查询到的结果set进page对象中
        page.setRecords(list);
        //也可以通过page调用相关方法获取到相关的分页信息,而且还可以把查询到的结果set回page对象中,方便前端使用。
    }

运行结果

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17aa8a11] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1963906615 wrapping com.mysql.cj.jdbc.ConnectionImpl@5190010f] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) FROM User
==> Parameters: 
<==    Columns: COUNT(*)
<==        Row: 31
<==      Total: 1
==>  Preparing: SELECT id,name,sex,pwd,email FROM User LIMIT ?
==> Parameters: 10(Long)
<==    Columns: id, name, sex, pwd, email
<==        Row: 2, test1.1,, aaaa, 1231@qq.com
<==        Row: 3, test2,, aaaa, 1232@qq.com
<==        Row: 4, test3,, aaaa, 1233@qq.com
<==        Row: 5, test4,, aaaa, 1234@qq.com
<==        Row: 6, test5,, aaaa, 1235@qq.com
<==        Row: 7, test6,, aaaa, 1236@qq.com
<==        Row: 8, test7,, aaaa, update@qq.com
<==        Row: 9, test8,, aaaaaa, 1238@qq.com
<==        Row: 42, test,, aaa, a@qq.com
<==        Row: 43, test,, aaa, a@qq.com
<==      Total: 10
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@17aa8a11]
com.baomidou.mybatisplus.extension.plugins.pagination.Page@44286963
================= 相关的分页信息 ==================
总条数:31
当前页码:1
总页数:4
每页显示条数:10
是否有上一页:false
是否有下一页:true
查询结果:
User(id=2, name=test1.1, sex=, pwd=aaaa, email=1231@qq.com)
User(id=3, name=test2, sex=, pwd=aaaa, email=1232@qq.com)
User(id=4, name=test3, sex=, pwd=aaaa, email=1233@qq.com)
User(id=5, name=test4, sex=, pwd=aaaa, email=1234@qq.com)
User(id=6, name=test5, sex=, pwd=aaaa, email=1235@qq.com)
User(id=7, name=test6, sex=, pwd=aaaa, email=1236@qq.com)
User(id=8, name=test7, sex=, pwd=aaaa, email=update@qq.com)
User(id=9, name=test8, sex=, pwd=aaaaaa, email=1238@qq.com)
User(id=42, name=test, sex=, pwd=aaa, email=a@qq.com)
User(id=43, name=test, sex=, pwd=aaa, email=a@qq.com)

可以看到通过分页插件 sql语句中已经有了limit,是物理分页了。并且是否有上一页下一页也有展示非常好用

3、自定义分页条件

示例:
只需要在你的 UserMapper.java 中加入你自定义的分页方法,也可以按你自定义方法实现分页。UserMapper可以继承或者不继承BaseMapper

@Mapper
//表明这是一个Mapper,也可以在启动类上加上包扫描
//Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
public interface UserMapper extends BaseMapper<User> {
    /**
     * 查询 : 根据性别查询用户列表,分页显示
     * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象)
     * @param sex 性别
     * @return 分页对象
     */
    @Select("SELECT * FROM user WHERE sex = #{sex}")
    IPage<User> selectPageBySex(Page<?> page, String sex);
}

测试代码

    @Test
    public void Test3() {
        Page<User> page = new Page<>(2, 10);
        IPage<User> selectPage = userMapper.selectPageBySex(page, "男");
        System.out.println("================= 相关的分页信息 ==================");
        System.out.println("总条数:" + selectPage.getTotal());
        System.out.println("当前页码:" + selectPage.getCurrent());
        System.out.println("总页数:" + selectPage.getPages());
        System.out.println("每页显示条数:" + selectPage.getSize());
        System.out.println("是否有上一页:" + page.hasPrevious());
        System.out.println("是否有下一页:" + page.hasNext());
        System.out.println("分页结果");
        selectPage.getRecords().forEach(System.out::println);
    }

运行结果

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6a0f2853] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1312762120 wrapping com.mysql.cj.jdbc.ConnectionImpl@7e87ef9e] will not be managed by Spring
==>  Preparing: SELECT COUNT(*) FROM user WHERE sex = ?
==> Parameters:(String)
<==    Columns: COUNT(*)
<==        Row: 17
<==      Total: 1
==>  Preparing: SELECT * FROM user WHERE sex = ? LIMIT ? OFFSET ?
==> Parameters:(String), 10(Long), 10(Long)
<==    Columns: id, name, sex, pwd, email
<==        Row: 52, test28,, aaaa, a28@qq.com
<==        Row: 54, test30,, aaaa, a30@qq.com
<==        Row: 56, test32,, aaaa, a32@qq.com
<==        Row: 58, test34,, aaaa, a34@qq.com
<==        Row: 60, test36,, aaaa, a36@qq.com
<==        Row: 62, test38,, aaaa, a38@qq.com
<==        Row: 64, Test,, aaaaa, 1234@qq.com
<==      Total: 7
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6a0f2853]
================= 相关的分页信息 ==================
总条数:17
当前页码:2
总页数:2
每页显示条数:10
是否有上一页:true
是否有下一页:false
分页结果
User(id=52, name=test28, sex=, pwd=aaaa, email=a28@qq.com)
User(id=54, name=test30, sex=, pwd=aaaa, email=a30@qq.com)
User(id=56, name=test32, sex=, pwd=aaaa, email=a32@qq.com)
User(id=58, name=test34, sex=, pwd=aaaa, email=a34@qq.com)
User(id=60, name=test36, sex=, pwd=aaaa, email=a36@qq.com)
User(id=62, name=test38, sex=, pwd=aaaa, email=a38@qq.com)
User(id=64, name=Test, sex=, pwd=aaaaa, email=1234@qq.com)

二、性能分析插件

这个插件PerformanceInterceptor在3.2.0被移除了,如果想进行性能分析,用第三方的,官方这样写的“该插件 3.2.0 以上版本移除推荐使用第三方扩展 执行 SQL 分析打印 功能

三、执行分析插件 – 防止全表更新与删除

1、配置插件

和上面的分页插件类似

    //BlockAttackInnerInterceptor
    //针对 update 和 delete 语句 作用: 阻止恶意的全表更新删除
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor3() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 执行分析插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }

2、测试代码

    @Test
    public void Test4(){
        //条件为null,就是删除全表,执行分析插件会终止该操作
        userMapper.delete(null);
    }

3、测试案例

运行该junit测试,可以看到报如下错误,说明该插件生效了。

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7e9f2c32] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@480645280 wrapping com.mysql.cj.jdbc.ConnectionImpl@49433c98] will not be managed by Spring
original SQL: DELETE FROM User
SQL to parse, SQL: DELETE FROM User
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7e9f2c32]

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion
### The error may exist in com/king/mybatis_plus/mapper/UserMapper.java (best guess)
### The error may involve com.king.mybatis_plus.mapper.UserMapper.delete
### The error occurred while executing an update
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion

四、乐观锁插件

1、什么是乐观锁

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。

2、mybatis-plus 实现方式

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

3、配置插件

  //乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor4() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

4、测试案例

  1. 表添加version列,并设置默认值为1。
    在这里插入图片描述
  2. 实体类添加version字段,并添加@Version注解。
    在这里插入图片描述
  3. 正常测试,更新数据时会校验版本号,新旧版本号一致时,表示当前数据未被其他事务更新过,更新通过,版本号自动+1;
    @Test
    public void Test5() {

        // 查询一条记录
        User user = userMapper.selectById(2);
        //更新
        user.setName("version");
        userMapper.updateById(user);

//        或者
//        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//        updateWrapper.set("name", "version");
//        updateWrapper.eq("id",2);
//        userMapper.update(user, updateWrapper);
    }

在这里插入图片描述

  1. 模拟并发

    开启DUG模式,首先查询出这条数据,版本号为2,断点暂停程序执行。然后手动修改此条数据的版本为4。放行程序,此时更新会校验版本号,但是版本号已经被修改,更新语句执行了,但是返回0,表示此次更新未生效,乐观锁功能验证成功。
    在这里插入图片描述
    在这里插入图片描述

mybatis-plus的扩展功能

一、自动填充

我们知道,当我们进行插入或者更新操作时,没有设置值的属性,那么在数据表中要么是为null,要么是保留原来的值。有的时候我们我们没有赋值但是却不想让其为空,比如updateTime,createTime属性,我们插入时会默认赋上 当前时间,更新时会默认赋值上当前时间,那么就可以用公共字段自动填充。

1、数据库表增加两个字段

在这里插入图片描述

2、使用@TableField注解标记填充字段

    //数据库与java类型对照表
    //
    //LocalTime 对应 time
    //LocalDate 对应 date
    //LocalDateTime 对应 datetime(timestamp)

    //创建时间
    @TableField(fill = FieldFill.INSERT)//表示插入数据时填充
    private LocalDateTime createTime;
    //上次修改时间
    @TableField(fill = FieldFill.INSERT_UPDATE)//插入和更新时填充
    private LocalDateTime updateTime;

3、编写公共字段填充处理器类 MyMetaObjectHandler

这里官网有介绍并且有多种写法

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info(" -------------------- start insert fill-----------------------");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);

        // 起始版本 3.3.3(推荐)
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info(" -------------------- start update fill ----------------------- ");
        //this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
        //使用上面的填充策略,当数据库字段不为空时将不填充
        //使用下面的填充策略,不论数据库中updateTime字段是否有值,都将该值更新。
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}

注: 该类继承了MetaObjectHandler类,重写了insertFill和updateFill方法,在这两个方法获取需要填充的字段以及默认填充的值。

4、测试

    @Test
    @DisplayName("自动填充插入测试")
    public void test1(){
        User user = User.builder().id(1).name("胡歌").sex("男").build();
        user.insert();
        System.out.println("插入后");
        user.selectById(1).println();
    }

运行结果
在这里插入图片描述

    @Test
    @DisplayName("自动填充修改测试")
    public void test2() {
        User user = User.builder().build().selectById(1);
        User user1 = user;
        user.println();
        user.setName("李逍遥1");
        user.setPwd("aaaaa");
        user.updateById();
        User user2 = user.selectById(1);
        System.out.println("==========修改前==========");
        user1.println();
        System.out.println("==========修改后==========");
        user2.println();
    }

在这里插入图片描述
可以看到实现了更新是自动更新时间

二、逻辑删除

其实数据并不会轻易的删除掉,毕竟数据收集不易,所以就有了逻辑删除。逻辑删除: 并不会真正的从数据库中将数据删除掉,而是将当前被删除的这条数据中的一个逻辑删除字段置为删除状态,比如该数据有一个字段flag,当其值为0表示未删除,值为1表示删除,那么逻辑删除就是将0变成1。

1、数据库添加字段flag

在这里插入图片描述

2、配置com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig

  1. application.yml

在这里插入图片描述

  global-config:
    db-config:
      logic-delete-field: flag  # 全局逻辑删除的实体字段名
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  1. 实体类增加字段

在这里插入图片描述

3、测试

@Test
    @DisplayName("逻辑删除测试")
    public void test3() {
        User user = User.builder().build().selectById(1);
        user.deleteById();
        User user2 = User.builder().build().selectById(1);
        System.out.println("======删除后======");
        Assertions.assertNull(user2);
    }

在这里插入图片描述
可见逻辑删除后,这个数据也是查不到的。

未完待续。。。

最后附上我的演示例源码

Github https://github.com/KingJin-web/springboot/tree/master/mybatis_plus

Gitee https://gitee.com/KingJin-web/springboot/tree/master/mybatis_plus
以上内容属于个人笔记整理,如有错误,欢迎批评指正!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值