MyBatis-Plus 乐观锁 防止超卖、逻辑删除、自动填充、Id自增

2022-10-31 14:01:10 浏览数 (1)

MyBatis-Plus 乐观锁 防止超卖、逻辑删除、自动填充 Day3

前面的简单的讲了一下mybatis-plus的使用 当然有很多不足 我写博客就是想促进大家一起学习 也想让这些内容更简单一些。

介绍

这次就主要讲乐观锁、逻辑删除、自动填充。这几项在项目是用的非常多的。

先讲一下主要应用 之后再讲理论和实现。

  1. 乐观锁: 主要用于防止商品超卖的方面
  2. 逻辑删除: 逻辑删除主要是用于用户对于数据的误删的一种撤销机制。 删除分为两种
    • 一种是物理删除 就是数据库层面的删除 彻底的从磁盘中删除
    • 另外一种就是今天讲的 逻辑删除 删除后还是会在数据库里保存着 只是查询的时候 需要带上参数才能查到
    • 当然彻底删除的时候也是需要带上那个参数才能彻底删除的。
  3. 自动填充:
    • 我之前看阿里的那个规范的时候 有看到就是说在数据库里面建立每一张表 都需要有创建时间和修改时间
    • 所以MP就提供自动填充的功能,帮助自定设置这些字段的值,提升开发效率,代码也会显得特别优雅。

乐观锁

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

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

能够保证数据的安全性和一致性。

因为后面还有逻辑删除、自动填充 环境就全部搭好拉。

使用方法

字段上加上@Version注解

代码语言:javascript复制
@Version
private Integer version;

mybatis配置注入下面这个bean

代码语言:javascript复制
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }

环境搭建 :

代码语言:javascript复制
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `version` int(10) NULL DEFAULT 1 COMMENT '乐观锁',
  `deleted` int(10) NULL DEFAULT 0 COMMENT '逻辑删除',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1337575143814062086 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

要导入的依赖 这里就不说了 我之前的博客也有。

user层

代码语言:javascript复制
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName(value = "user")
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;
}

mapper层

代码语言:javascript复制
public interface UserMapper extends BaseMapper<User> {

}

MyBatisPlusConfig

代码语言:javascript复制
@Configuration
public class MybatisPlusConfig {

    // 分页
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 这里是注册分页插件
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 注册乐观锁 插件
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }    
}

yaml

代码语言:javascript复制
server:
  port: 8484
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useSSL=false&characterEncoding=utf8&serverTimezone=GMT

乐观锁测试

先测试一下 没有加乐观锁的 会是什么样

代码语言:javascript复制
    @Test
    public  void updateOptimisticLockerInterceptor(){
        //A 线程
        User user = userMapper.selectById(102L);
        user.setName("点赞1");
        user.setAge(3);
        //B 线程  模拟另外一个线程插队
        User user1 = userMapper.selectById(102L);
        user1.setName("收藏加三连");
        user1.setAge(4);
        userMapper.updateById(user1);
        // 自旋锁来尝试多次提交
        userMapper.updateById(user);  // 如果没有乐观锁 就会覆盖插队的值
    }

下面给大家看一下控制台的输出

为了方便观看 我把测试数据改了 来接着看一下这次的结果

代码语言:javascript复制
    @Test
    public  void updateOptimisticLockerInterceptor(){
        //A 线程
        User user = userMapper.selectById(102L);
        user.setName("关注宁在春");
        user.setAge(3);
        //B 线程  模拟另外一个线程插队
        User user1 = userMapper.selectById(102L);
        user1.setName("给宁在春点赞");
        user1.setAge(4);
        userMapper.updateById(user1);
        // 自旋锁来尝试多次提交
        userMapper.updateById(user);  // 如果没有乐观锁 就会覆盖插队的值
    }

这就是加了乐观锁的作用 在多线程下 可以保证数据的安全 防止商品超卖等等。

逻辑删除

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

使用方法:

步骤1: 配置
  • 在application.yml 加入下面配置
代码语言:javascript复制
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
步骤2: 实体类字段上加上@TableLogic注解
代码语言:javascript复制
@TableLogic
private Integer deleted;	

逻辑删除 测试

代码语言:javascript复制
// TODO tableLogic 逻辑删除
@Test
public void tableLogic(){
    userMapper.deleteById(103);
    List<User> userList = userMapper.selectList(null);
    userList.forEach(System.out::println);
}

测试结果如下 使用查询全部方法 明显查不到这行数据拉 我们接下来看看 数据库里的表 看还有没有

数据库里面还存在 只是deleted 变成1拉

自动填充

原理:

  • 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
  • 注解填充字段 @TableField(.. fill = FieldFill.INSERT) 生成器策略部分也可以配置!
代码语言:javascript复制
public class User {

    // 注意!这里需要标记为填充字段
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

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

}

自定义实现类 MyMetaObjectHandler

代码语言:javascript复制
@Slf4j
@Component
public class MyMetaObjectHandler 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("start update fill ....");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}

测试

我们来增加一行数据看一看会不会吧 这里把Id 自增也讲一起讲了吧

mybatisplus 默认的主键自增 是默认使用雪花算法 UUID(不含中划线)

具体的 大家可以自行研究。

代码语言:javascript复制
//TODO insert 配置了自动填充后的insert
@Test
public void insert() {
    User user = new User().setName("自动填充").setAge(99).setEmail("ssss@qq.com");
    int i = userMapper.insert(user);
    System.out.println(i);
    Map<String, Object> map = new HashMap<String,Object>();
    map.put("name","自动填充");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

测试图:

搞定

溜了溜了

大家看的好 就评论评论 一起学习学习咯

刚开始 文笔不咋好 争取之后 我加油写的好一些 再来点故事。

0 人点赞