MyBatis-Plus 乐观锁 防止超卖、逻辑删除、自动填充 Day3
前面的简单的讲了一下mybatis-plus的使用 当然有很多不足 我写博客就是想促进大家一起学习 也想让这些内容更简单一些。
介绍
这次就主要讲乐观锁、逻辑删除、自动填充。这几项在项目是用的非常多的。
先讲一下主要应用 之后再讲理论和实现。
- 乐观锁: 主要用于防止商品超卖的方面
- 逻辑删除:
逻辑删除主要是用于用户对于数据的误删的一种撤销机制。
删除分为两种
- 一种是物理删除 就是数据库层面的删除 彻底的从磁盘中删除
- 另外一种就是今天讲的 逻辑删除 删除后还是会在数据库里保存着 只是查询的时候 需要带上参数才能查到
- 当然彻底删除的时候也是需要带上那个参数才能彻底删除的。
- 自动填充:
- 我之前看阿里的那个规范的时候 有看到就是说在数据库里面建立每一张表 都需要有创建时间和修改时间
- 所以MP就提供自动填充的功能,帮助自定设置这些字段的值,提升开发效率,代码也会显得特别优雅。
乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
能够保证数据的安全性和一致性。
因为后面还有逻辑删除、自动填充 环境就全部搭好拉。
使用方法
字段上加上@Version
注解
@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 加入下面配置
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)
生成器策略部分也可以配置!
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);
}
测试图:
搞定
溜了溜了
大家看的好 就评论评论 一起学习学习咯
刚开始 文笔不咋好 争取之后 我加油写的好一些 再来点故事。