【MybatisPlus】简介+入门+CRUD+乐观锁插件+分页插件+逻辑删除


简介

  1. 官方网站:https://mybatis.plus/
    在这里插入图片描述

  2. 根据名字我们就可以看出来MybatisPlus是Mybatis的增强版,事实上也是如此,MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

  3. 有哪些特性?

特性解释
无侵入只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
  1. 架构结构

在这里插入图片描述

入门案例

数据库

  1. 建表
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)
);
  1. 插入数据
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');

IDEA新建项目

环境配置

  1. 新建一个Springboot项目,勾选web依赖。
  2. 除了新建项目时已经导入的web依赖,还需要导入的依赖:
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
  1. 数据库配置文件:application.yaml
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2b8
    driver-class-name: com.mysql.cj.jdbc.Driver

入门案例

  1. 我们看到mybatis-plus的特性之一:强大的 CRUD 操作,内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求。
  2. 以前项目结构:pojo–dao–service,运用到mybatis-plus可以省去mapper.xml和service层。(针对简单的CRUD项目,复杂的还是需要自己写。)
  3. 对应前面建立的数据库用户表,编写实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  1. 编写实体类对应的Mapper接口:记住需要加注解@Mapper让我们的容器能够扫描到,同时需要继承BaseMapper<T >,T是我们需要操作的实体类,继承BaseMapper类后,mybatis-plus会帮我们写完CRUD操作。
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {

    //extends BaseMapper<User>===》CRUD操作已经做完

}

  1. 测试:

我们Mapper接口一个方法都没有写,只是继承了BaseMapper类,我们在测试类中自动装配Mapper接口,可以看到下图MybatisPlus给我们实现的CRUD操作。
在这里插入图片描述

简单测试一下查询所有用户操作:

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        //查询全部用户
       // 参数是一个 wrapper ,条件构造器,这里我们先不用nuLl
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
        
    }

}

运行结果:

在这里插入图片描述

配置日志

  1. 在application.yaml中配置:
#配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  1. 上面程序配置日志后,可以看到是怎么实现的:

在这里插入图片描述

插入操作

主键生成策略

延用上面程序,进行插入操作:

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setName("高朗");
        user.setAge(18);
        user.setEmail("3211@qq.com");
        userMapper.insert(user);
    }

}

可以看到我没有设置ID,也就是数据库的主键,运行:

在这里插入图片描述

我们可以看到程序为我们自动生成了一个ID,这就是Mybatis-Plus的主键生成,那么为什么会生成一个这样的ID,其实内部实现采用雪花算法。

雪花算法(SnowFlake)

snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是∶使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),最后还有一个符号位,永远是0。

生成主键的其他方式

采用注解形式:在主键上加@TableId注解,并设置注解的type值:

可以设置的type值:

IdType:

描述
AUTO数据库ID自增
NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUTinsert前自行set主键值
ASSIGN_ID分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
ASSIGN_UUID分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
ID_WORKER分布式全局唯一ID 长整型类型(please use ASSIGN_ID)
UUID32位UUID字符串(please use ASSIGN_UUID)
ID_WORKER_STR分布式全局唯一ID 字符串类型(please use ASSIGN_ID)
AUTO(自动增长)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

注意这里主键自增需要和数据库主键相同,也就是说数据库中主键也需要设置为自增,不然会报错。

运行上面插入的测试代码:

在这里插入图片描述

发现sql语句中没有Id,打开数据库表就可以发现ID是自增的:在这里插入图片描述

NONE(无状态)

如果使用IdType.NONE策略,表示未设置主键类型。

Input(自定义输入)

自定义IdType.INPUT策略,表示我们需要设置主键的值,不然不会生成为null。

ASSIGN_ID(雪花算法)

默认的策略,主键类型Long或String。

ASSIGN_UUID(不含中划线的UUID)

如果使用IdType.ASSIGN_UUID策略,则会自动生成不含中划线的UUID作为主键,主键类型为String。

更新操作

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setId(6L);
        user.setName("小明");
        user.setAge(19);
        userMapper.updateById(user);
    }

}

运行:
在这里插入图片描述

自动填充

某条数据的创建时间和更新时间,我们不希望自己动手写,就可以用到自动填充。

方式一:数据库级别

  1. 数据库表增加两个属性字段:create_time 和update_time

ALTER TABLE user ADD create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';
ALTER TABLE user ADD update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间';
  1. 实体类同步:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    private Date createTime;
    private Date updateTime;
}
  1. 测试

(a)更新操作:

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setId(6L);
        user.setName("潇潇");
        user.setAge(22);
        userMapper.updateById(user);
    }

}

运行代码,查看数据库表,我们更新的数据的update_time字段自动更新为电脑的时间
在这里插入图片描述
(b)增加操作

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setName("高朗");
        user.setAge(22);
        user.setEmail("111@qq.com");
        userMapper.insert(user);
    }

}

运行程序,查看数据库表,create_time和update_time两个字段为电脑时间:

在这里插入图片描述

方式二:代码级别

  1. 数据库操作:把create_time和update_time改成普通的datetime类型字段属性。
ALTER TABLE `user` DROP COLUMN create_time;
ALTER TABLE `user` DROP COLUMN update_time;
ALTER TABLE user ADD create_time datetime COMMENT '创建时间';
ALTER TABLE user ADD update_time datetime COMMENT '更新时间'; 
  1. 对应实体类属性字段上加上注解@TableField(fill = FieldFill.XXX)

FieldFill 枚举:

public enum FieldFill {
    /**
     * 默认不处理
     */
    DEFAULT,
    /**
     * 插入填充字段
     */
    INSERT,
    /**
     * 更新填充字段
     */
    UPDATE,
    /**
     * 插入和更新填充字段
     */
    INSERT_UPDATE
}

实体类注解:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
}
  1. 编写处理器处理注解:实现MetaObjectHandler 接口
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {

        //setFieldValByName(String fieldName, Object fieldVal, MetaObject 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("start update fill ....");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
  1. 测试:

(a)插入操作

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setName("Linda");
        user.setAge(18);
        user.setEmail("222@qq.com");
        userMapper.insert(user);
    }

}

运行:

在这里插入图片描述

(b)更新操作

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        User user = new User();
        user.setId(10L);
        user.setAge(20);
        user.setEmail("333@qq.com");
        userMapper.updateById(user);
    }

}

在这里插入图片描述

乐观锁

  1. 乐观锁:故名思意十分乐观,它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试。
  2. 悲观锁︰故名思意十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!
  3. 我们MybatisPlus采用的是乐观锁插件:

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

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

MybatisPlus乐观锁插件

  1. 在数据库表中增加一个 version字段,并设置默认值1
ALTER TABLE `user` ADD version INT(5) DEFAULT 1
  1. 实体类加对应属性,并添加乐观锁@Version注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @Version
    private Integer version;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

}
  1. 注册组件
@Configuration
public class MybatisPlusConfig {


 //乐观锁添加方式一:
   @Bean
    public MybatisPlusInterceptor MybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分页设置
        //mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());

        return mybatisPlusInterceptor;
    }

//    //乐观锁添加方式二:
//    @Bean
//    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor(){
//        return new OptimisticLockerInnerInterceptor();
//    }


}
  1. 测试

(a)成功测试:

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        //Step1:查询用户
        User user = userMapper.selectById(10L);
        //Step2:修改用户
        user.setName("小明");
        user.setAge(18);
        userMapper.updateById(user);
    }

}

结果:版本号会自动更新
在这里插入图片描述
在这里插入图片描述
(b)失败测试

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        //Step1:对同一个用户更新操作
        User user = userMapper.selectById(10L);
        User user2 = userMapper.selectById(10L);
        //Step2:修改用户1
        user.setName("高朗");
        user.setAge(17);
        //Step3:修改用户2
        user2.setName("Linda");
        user2.setAge(16);
        
        userMapper.updateById(user);
        userMapper.updateById(user2);
    }

}

结果:第二条更新没有操作。(第一条更新完后,版本加一,3,而第二条更新时版本为2对应不了数据库中的版本3,更新失败。

在这里插入图片描述
在这里插入图片描述

查询操作

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;


    //根据ID查询
    @Test
    public void selectTest(){
        //查询ID为1的
        User user = userMapper.selectById(1);
        System.out.println(user);
    }

    //批量查询
    @Test
    public void selectTest2(){
        //查询ID为1,2,3的
        List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        userList.forEach(System.out::println);
    }

    //条件查询 map
    @Test
    public void selectTest3(){
        HashMap<String, Object> map = new HashMap<>();

        //查询年龄为20的用户  age为数据库的字段名
        map.put("age",20);

        List<User> userList = userMapper.selectByMap(map);
        userList.forEach(System.out::println);
    }

}

分页查询

  1. 配置MybatisPlus分页插件:
@Configuration
public class MybatisPlusConfig {

   @Bean
    public MybatisPlusInterceptor MybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //乐观锁
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        //分页设置
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());

        return mybatisPlusInterceptor;
    }

}
  1. 测试
@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void PageSelect(){
        //参数1:当前页数  参数二:页面大小
        Page<User> page = new Page<>(1,5);

        userMapper.selectPage(page, null);

        //打印当前页的数据
        page.getRecords().forEach(System.out::println);

        //打印数据库中的数据总数
        System.out.println(page.getTotal());


    }

}

运行:可以看到是采用LIMIT实现的
在这里插入图片描述

删除操作

和查询操作类似,有根据ID删除,根据ID批量删除,map根据条件删除:
在这里插入图片描述

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;


    //根据ID删除
    @Test
    public void deleteTest(){
        //删除ID为1的
        userMapper.deleteById(1);
    }

    //批量删除
    @Test
    public void deleteTest2(){
        //查询ID为2,3,4的
        userMapper.deleteBatchIds(Arrays.asList(2, 3, 4));
    }

    //条件删除 map
    @Test
    public void deleteTest3(){
        HashMap<String, Object> map = new HashMap<>();

        //删除年龄为20的用户  age为数据库的字段名
        map.put("age",20);

        userMapper.deleteByMap(map);
    }

}

逻辑删除

  1. 什么是逻辑删除?

(1)物理删除︰从数据库中直接移除。
(2)逻辑删除︰在数据库中没有被移除,而是通过一个变量来让他失效!
(3)相当于回收站,逻辑删除后,等同于放入回收站,回收站是可以恢复的。
(4)数据库表中可以添加一个deleted字段,默认值为0,逻辑删除后为1。

  1. 使用流程:

(1)数据库中加一个deleted字段

ALTER TABLE `user` ADD deleted INT(5) DEFAULT 0

(2)实体类加对应属性,并添加乐观锁@@TableLogic注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    @Version
    private Integer version;

    @TableLogic
    private Integer deleted;

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

}

(3)配置
application.properties

# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

(4)测试:

    //根据ID删除
    @Test
    public void deleteTest(){
        //删除ID为1的
        userMapper.deleteById(1);
    }

运行:后台的操作变成了更新操作,而不是删除,而且对应的数据的deleted属性被修改成1
在这里插入图片描述

前面是逻辑删除,我们看看查询这个ID为1的数据,是否能够查询到:

    //根据ID查询
    @Test
    public void selectTest(){
        //查询ID为1的
        User user = userMapper.selectById(1);
        System.out.println(user);
    }

运行:查询不到,后台查询语句帮我们加了一个deleted=0的条件

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值