MP高级功能-乐观锁插件

简介

乐观锁的目的是:当要更新一条记录时,希望这条数据没有被别人更新过;为了防止更新冲突的问题。

乐观锁如何实现?

实现方式一:版本号方式

  • 取出记录时,获取当前version;
  • 更新时,带上这个version;
  • 版本正确更新成功,错误更新失败。

场景:

  • sql语句
update table set version=newVersion,x=a,y=b where version=oldVersion and z=c;

示例:

update user set name='向南天',version=3 where id=1094592041087729777 and version=2;

引申:

  1. 数据库中的锁有两种(乐观锁、悲观锁);
  2. 悲观锁是通过数据库的锁机制实现的;
  3. 两种锁各有优缺点,不能认为一种好于另一种;
  4. 乐观锁适用于写比较少的场景下,就是多读场景;因为多读场景即使有冲突,也很少会发生,这样省去了系统的开销,加大了系统的整体吞吐量;
  5. 但如果是多写的情况下,一般会经常产生冲突,就会导致上层应用不断的进行重试,这样反倒是降低了系统的性能,所以一般多写的场景用悲观锁比较合适,而多读的场景是用乐观锁比较合适。

功能实现:

一、实现步骤

  1. 配置乐观锁插件OptimisticLockerInterceptor:
package com.mp.first.configuration;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }

//    @Bean
//    public PaginationInterceptor paginationInterceptor(){
//        return new PaginationInterceptor();
//    }

//    3.1.1以下版本需要此配置,以上版本不需要此配置
//    @Bean
//    public ISqlInjector sqlInjector(){
//        return new LogicSqlInjector();
//    }
}
  1. 实体类中找到与数据库中列名version对应的变量名version,并给它加上注解@Version:
    @Version
    private int version;
  1. 测试
package com.mp.first;

import com.mp.first.dao.UserMapper;
import com.mp.first.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class OptTest {
    @Autowired
    private UserMapper userMapper;

//    SELECT id,name,age,email,manager_id,create_time,update_time,version FROM user WHERE id=? AND deleted=0 
//    UPDATE user SET name=?, age=?, email=?, manager_id=?, create_time=?, update_time=?, version=? WHERE id=? AND version=? AND deleted=0
    @Test
    public void updateById() {
        User user = userMapper.selectById(3l);
        user.setName("张大嘴");
        int rows = userMapper.updateById(user);
        System.out.println("影响行数:" + rows);
    }
}

二、注意事项

  • 支持的数据类型有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下:newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity,wrapper) 方法
  • 在update(entity,wrapper) 方法下, wrapper 不能复用!!!
update(entity,wrapper) 方法复用后出现问题示例:
//    UPDATE user SET name=?, age=?, update_time=?, version=? WHERE deleted=0 AND (age = ? AND version = ? AND version = ?) 
    @Test
    public void updateByEntity(){
        User user = userMapper.selectById(3L);
        user.setName("李大嘴");
        LambdaQueryWrapper lambdaWrapper = Wrappers.lambdaQuery().eq(User::getAge, 28);
        userMapper.update(user, lambdaWrapper);

        User u = new User();
        u.setName("王大锤");
        userMapper.update(u, lambdaWrapper);
    }

可以看到生成的sql中,条件语句里多了两个version=?,这样会导致更新语句更新不成功。

你可能感兴趣的:(MP高级功能-乐观锁插件)