【谷粒学院】002-Mybatis Plus:主键策略、实现自动填充、乐观锁

目录

一、主键策略

1、ID_WORKER

2、自增策略

3、其它主键策略

二、实现自动填充

1、根据Id更新操作

2、自动填充

数据库表中添加自动填充字段:

数据库表中添加create_time和update_time两个字段:

实体上添加注解:

创建类,实现MetaObjectHandler接口:

添加一条数据:

神奇的事情发生了:

注意:

三、乐观锁

1、什么是乐观锁

2、什么叫丢失更新

3、使用Mybatis Plus实现乐观锁

主要适用场景:

乐观锁实现方式:

步骤:

(1)在数据库中添加version字段:

(2)在实体类添加version字段,并添加 @Version 注解

(3)使用元对象处理器接口添加version的insert默认值

(4)在 MybatisPlusConfig 中注册 Bean

(5)测试乐观锁插件


一、主键策略

1、ID_WORKER

MyBatis-Plus默认的主键策略是:ID_WORKER 全局唯一ID;

参考资料:分布式系统唯一ID生成方案汇总分布式系统唯一ID生成方案汇总 - nick hao - 博客园

(自动增长、UUID、redis实现、Mybatis Plus自带策略(snowflake雪花算法))

#主键随机生成策略 #主键策略

2、自增策略

要想主键自增需要配置如下主键策略:

①需要在创建数据表的时候设置主键自增;

②实体字段中配置 @TableId(type = IdType.AUTO);

@TableId(type = IdType.AUTO)
private Long id;

要想影响所有实体的配置,可以设置全局主键配置:

#全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto

3、其它主键策略

(分析 IdType 源码可知)

@Getter
public enum IdType {
    /**
    * 数据库ID自增
    */
    AUTO(0),

    /**
    * 该类型为未设置主键类型
    */
    NONE(1),

    /**
    * 用户输入ID
    * 该类型可以通过自己注册自动填充插件进行填充
    */
    INPUT(2),

    /* 以下3种类型、只有当插入对象ID 为空,才自动填充。 */
    /**
    * 全局唯一ID (idWorker)
    */
    ID_WORKER(3),

    /**
    * 全局唯一ID (UUID)
    */
    UUID(4),

    /**
    * 字符串全局唯一ID (idWorker 的字符串表示)
    */
    ID_WORKER_STR(5);

    private int key;

    IdType(int key) {
        this.key = key;
    }
}

二、实现自动填充

1、根据Id更新操作

注意:update时生成的sql自动是动态sql:UPDATE user SET age=? WHERE id=?

package com.zibo.mybatisplus;

import com.zibo.mybatisplus.entity.User;
import com.zibo.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MybatisPlusApplicationTests {

    //注意:
    //IDEA在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
    //为了避免报错,可以在 dao 层 的接口上添加 @Repository 注解
    @Autowired
    private UserMapper userMapper;

    //查询操作
    @Test
    public void contextLoads() {
        System.out.println(("----- selectAll method test ------"));
        //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper
        //所以不填写就是无任何条件
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }

    //添加操作
    @Test
    public void add(){
        //组织user对象
        User user = new User();
        user.setName("zibo");
        user.setAge(24);
        user.setEmail("18838186892@163.com");
        //添加到数据库
        int insert = userMapper.insert(user);
        System.out.println("insert:" + insert);//影响的行数
        System.out.println(user);//id自动回填
    }

    //修改操作
    @Test
    public void update(){
        //组织user对象
        User user = new User();
        user.setId(1L);
        user.setAge(28);
        //通过id更新该user对象
        int updateById = userMapper.updateById(user);
        System.out.println("updateById:" + updateById);//影响的行数
    }

}

2、自动填充

项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等;
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:

数据库表中添加自动填充字段:

在User表中添加datetime类型的新的字段 create_time、update_time;

数据库表中添加create_time和update_time两个字段:

实体上添加注解:

package com.zibo.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.util.Date;

@Data
public class User {
    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;
}

创建类,实现MetaObjectHandler接口:

package com.zibo.mybatisplus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //创建填充
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);//metaObject元数据
        this.setFieldValByName("updateTime",new Date(),metaObject);//metaObject元数据
    }

    //更新填充
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);//metaObject元数据
    }
}

添加一条数据:

    //添加操作
    @Test
    public void add(){
        //组织user对象
        User user = new User();
        user.setName("zb");
        user.setAge(24);
        user.setEmail("18838186892@163.com");
        //添加到数据库
        int insert = userMapper.insert(user);
        System.out.println("insert:" + insert);//影响的行数
        System.out.println(user);//id自动回填
    }

神奇的事情发生了:

注意:

自动填充不是必然的,创建时间和更新时间也可以像其他属性(例如姓名、年龄等一样直接set);

user.setCreateTime(new Date());
user.setUpdateTime(new Date());

三、乐观锁

1、什么是乐观锁

主要用来解决丢失更新的问题;

如果不考虑事务的隔离性,会产生读问题:

脏读、不可重复读、幻读;

写问题:丢失更新;

2、什么叫丢失更新

并发情况下,多个线程同时修改同一数据,之后提交的数据会把之前提交的数据覆盖;

解决方案:悲观锁(一般不用)、乐观锁;

悲观锁:一个线程操作数据的时候其他线程都不能操作(串行);

乐观锁:为要修该的数据设置一个版本号字段,改之前比较当前数据版本号与数据库中版本号是否一致,一致则进行修改,并将版本号+1;

3、使用Mybatis Plus实现乐观锁

主要适用场景:

当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新;

乐观锁实现方式:

取出记录时,获取当前version;

更新时,带上这个version;

执行更新时, set version = newVersion where version = oldVersion;

如果version不对,就更新失败;

步骤:

(1)在数据库中添加version字段:

ALTER TABLE `user` ADD COLUMN `version` INT

(2)在实体类添加version字段,并添加 @Version 注解

package com.zibo.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

import java.util.Date;

@Data
public class User {
    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;
    //版本号:用于乐观锁
    @Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version;
}

(3)使用元对象处理器接口添加version的insert默认值

package com.zibo.mybatisplus.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

//元对象处理器
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    //创建填充
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime",new Date(),metaObject);//metaObject元数据
        this.setFieldValByName("updateTime",new Date(),metaObject);//metaObject元数据
        //添加版本号
        this.setFieldValByName("version", 1, metaObject);
    }

    //更新填充
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime",new Date(),metaObject);//metaObject元数据
    }
}   

特别说明:

支持的数据类型只有 int、Integer、long、Long、Date、Timestamp、LocalDateTime;

整数类型下 newVersion = oldVersion + 1;

newVersion 会回写到 entity 中;

仅支持 updateById(id) 与 update(entity, wrapper) 方法;

在 update(entity, wrapper) 方法下,wrapper 不能复用;

(4)在 MybatisPlusConfig 中注册 Bean

创建config包——创建MybatisPlusConfig类

(此时可以删除主类中的 @MapperScan 扫描注解)

package com.zibo.mybatisplus.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@EnableTransactionManagement
@Configuration
@MapperScan("com.zibo.mybatisplus.mapper")
public class MybatisPlusConfig {
    /*
     * 乐观锁插件旧版本,已过时
     */
//    @Bean
//    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
//        return new OptimisticLockerInterceptor();
//    }
    /**
     * 乐观锁插件新版本
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

(5)测试乐观锁插件

先添加一条新数据,然后修改这条数据,看效果:

添加:

    //添加操作
    @Test
    public void add(){
        //组织user对象
        User user = new User();
        user.setName("zb002");
        user.setAge(24);
        user.setEmail("18838186892@163.com");
        //添加到数据库
        int insert = userMapper.insert(user);
        System.out.println("insert:" + insert);//影响的行数
        System.out.println(user);//id自动回填
    }

修改:

    /**
     * 测试 乐观锁插件
     */
    @Test
    public void testOptimisticLocker() {
        //查询
        User user = userMapper.selectById(1332883585346080769L);
        //修改数据
        user.setName("zb002");
        user.setEmail("zb002@qq.com");
        //执行更新
        userMapper.updateById(user);
    }

结果:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值