当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
1:取出记录时,获取当前 version
2:更新时,带上这个 version 执行更新时, set version = newVersion
where version = oldVersion 如果 version 不对,就更新失败
一、乐观锁插件实现步骤:
1、配置插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
2、在实体类的字段上加上@Version注解
/**
* 版本
*/
@Version//版本号,用于实现乐观锁(这个一定要加)
@TableField(fill = FieldFill.INSERT)//添加这个注解是为了在后面设置初始值,不加也可以
private Integer version;
3、配置MyMetaObjectHandler
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
//设置版本号version的初始值为1
//不加这个也可以,version的默认值为null,加了就是设置version的值从1开始
this.setFieldValByName("version",1,metaObject);
}
//使用mp实现修改的操作,这个方法就会执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
4、测试代码及结果
@SpringBootTest
class DemoApplicationTests {
@Resource
UserMapper userMapper;
@Test
public void testAdd() {
User user = new User();
user.setName("OrangeTest9");
user.setAge(22);
user.setEmail("1525@qq.com");
int insert = userMapper.insert(user);
System.out.println(insert);
}
/**
* 乐观锁实现方式:
* 1、取出记录时,获取当前version
* 2、更新时,带上这个version
* 3、执行更新时, set version = newVersion where version = oldVersion
* 4、如果version不对,就更新失败
*/
//测试乐观锁,看version的值是否加1
@Test
void testOptimisticLocker(){
//根据id查询数据
User user = userMapper.selectById(15);
//进行修改
user.setAge(21);
userMapper.updateById(user);
}
}
先执行testAdd,再执行testOptimisticLocker,观察version值变化。
测试乐观锁,模拟失败场景
//测试乐观锁,模拟失败场景
@Test
void testOptimisticLockerFail(){
//线程1
//根据id查询数据
User user1 = userMapper.selectById(3);
user1.setAge(20);
//线程2
User user2 = userMapper.selectById(3);
user2.setAge(28);
//模拟另一个线程执行了插队操作
userMapper.updateById(user2);
userMapper.updateById(user1);
}
执行结果:version已经更改,第二个SQL则执行失败!
二、配置逻辑删除
1、在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)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2、实体类字段
/**
* 删除标志
*/
@TableLogic
private Integer deleted;
3、执行查询发现自动过滤deleted = 0的数据
三、防全局删除和更新
业务bug或者漏洞可能导致把整个表都更新或者删除,在生产环境中这是十分危险的事情,plus也考虑到了这一点,提供了防止全表更新与删除插件。
@Configuration
@MapperScan("com.example.demo.*")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加防止全表更新与删除插件
BlockAttackInnerInterceptor blockAttackInnerInterceptor=new BlockAttackInnerInterceptor();
interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
return interceptor;
}
}
单元测试
//防止全表更新与删除插件
@Test
public void deleteAll() {
//执行会报错: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion
int delete = mapper.delete(null);
System.out.println("删除成功"+delete);
/* 改成根据条件删除,就不会报错*/
QueryWrapper<Users> queryWrapper = new QueryWrapper();
Users user = new Users();
user.setName("三丰丰");
queryWrapper.lambda().eq(Users::getName,"三丰丰");
mapper.delete(queryWrapper);
int delete2 = mapper.delete(null);
System.out.println("删除成功"+delete2);
}
@Test
public void updateAll() {
LambdaUpdateWrapper<Users> updateWrapper = new LambdaUpdateWrapper<Users>().set(Users::getName, "三丰丰");
mapper.update(null, updateWrapper);
System.out.println("修改成功");
}
防止全局删除结果1
防止全局更新2
参考文章
https://baomidou.com/pages/24112f/#%E7%89%B9%E6%80%A7