【Java】mybatis-plus乐观锁与Spring重试机制

上一篇【Java】mybatis-plus乐观锁-基本使用

讲到了mybatis-plus的基本使用,简单的使用@Version和一个基础配置类即可实现乐观锁。

但是mybatis-plus本身并没有自带重试机制。

即当我们带上版本号去更新数据,但是由于另一个线程已经将版本号修改了,导致这次的修改失败,那么应该重新读取数据,再次更新,这就是重试机制。

实现重试机制

使用spring-retry 库

这里可以使用spring-retry库来实现重试机制。spring-retry提供了方便的注解和配置,可以轻松地实现重试逻辑。注意事项:spring-retry由于是基于AOP实现,所以不支持类里自调用方法。注意:需要在启动类 上加@EnableRetry开启spring-retry库

引入依赖

xml 复制代码
<dependency>
   <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

config层、dao层、entity层同上一篇文章

Service层

这里修改对Service层进行修改,新增了一个带@Retryable的注解

这里注解的意思是,当遇到OptimisticLockingFailureException就进行重试,重试的最大次数的3次。

加上第一次报错,则这个方法最多会被执行4次

java 复制代码
@Service
public class UserService extends ServiceImpl<UserMapper, User> {

    /**
     * 新增余额
     * @param id 用户id
     * @param addBalance 需要新增的余额
     * @return 是否成功
     */
    @Retryable(value = {OptimisticLockingFailureException.class},maxAttempts = 3,backoff = @Backoff(delay = 2000))
    public boolean incrementBalance(Integer id,Integer addBalance){
        User user = getById(id);
        user.setBalance(user.getBalance()+addBalance);
        boolean flag = updateById(user);
        if (flag){
            System.out.println("更新成功");
        }else{
            System.out.println("更新失败");
            throw new OptimisticLockingFailureException("更新失败");
        }
        return true;
    }

    /**
     * 初始化用户余额
     * @param id 用户Id
     * @param balance 余额
     * @return 是否成功
     */
    public boolean setUserBalance(Integer id,Integer balance){
        User user = getById(id);
        user.setBalance(balance);
        boolean flag = updateById(user);
        if (flag){
            System.out.println("更新成功");
        }else{
            System.out.println("更新失败");
            throw new OptimisticLockingFailureException("更新失败");
        }
        return true;
    }

}

测试

在这里模拟了一个高并发场景:

  1. 给用户1初始化余额为0元
  2. 创建了15个线程,每个线程都对该用户余额+1
  3. 通过原子类来计算,成功次数,和失败次数。
  4. 期望:用户的最终余额=0+成功次数
java 复制代码
@SpringBootTest
public class MpApplicationTests {

    @Autowired
    private UserService userService;

    @Test
    public void test3() throws IOException {
        // 给用户1的余额初始化成 0元
        userService.setUserBalance(1,0);

        ExecutorService executorService = Executors.newFixedThreadPool(5);
        // 执行次数
        AtomicInteger count = new AtomicInteger();
        // 成功次数
        AtomicInteger successCnt = new AtomicInteger();
        // 失败次数
        AtomicInteger failCnt = new AtomicInteger();
        for (int i = 0; i < 15; i++) {
            executorService.submit(() -> {
                try {
                    // 给用户1,新增1元余额
                    userService.incrementBalance(1,1);
                    successCnt.incrementAndGet();
                }catch (OptimisticLockingFailureException e){
                    // 重试多次后还是失败,抛出乐观锁异常
                    failCnt.incrementAndGet();
                }
                count.incrementAndGet();
            });
        }
        while (count.get() <= 15){
            ThreadUtil.sleep(1000);
            System.out.println("执行完毕");
            System.out.println("执行次数:"+count.get()+" 成功:"+successCnt.get()+" 失败:"+failCnt.get());
            User user = userService.getById(1);
            System.out.println("用户最终余额:"+user.getBalance());
            if (count.get() == 15){
                break;
            }
        }
    }
}

测试结果如下,符合期望

复制代码
执行完毕
执行次数:15 成功:13 失败:2
用户最终余额:13

如果觉得这个更新成功率太低了,可以自行修改重试次数。当我修改成5次时,这15个并发基本能达到100%了,这个视情况而定

相关推荐
桦说编程6 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
lifallen6 小时前
Java Stream sort算子实现:SortedOps
java·开发语言
IT毕设实战小研6 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
记忆不曾留6 小时前
Mybatis 源码解读-SqlSession 会话源码和Executor SQL操作执行器源码
mybatis·二级缓存·sqlsession会话·executor执行器·一级缓存localcache
没有bug.的程序员6 小时前
JVM 总览与运行原理:深入Java虚拟机的核心引擎
java·jvm·python·虚拟机
甄超锋7 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国7 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Zyy~7 小时前
《设计模式》装饰模式
java·设计模式
A尘埃7 小时前
企业级Java项目和大模型结合场景(智能客服系统:电商、金融、政务、企业)
java·金融·政务·智能客服系统
青云交8 小时前
Java 大视界 -- 基于 Java 的大数据可视化在城市交通拥堵治理与出行效率提升中的应用(398)
java·大数据·flink·大数据可视化·拥堵预测·城市交通治理·实时热力图