当前位置: 首页 > java >正文

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

上一篇【Java】mybatis-plus乐观锁-基本使用
讲到了mybatis-plus的基本使用,简单的使用@Version和一个基础配置类即可实现乐观锁。
但是mybatis-plus本身并没有自带重试机制。
即当我们带上版本号去更新数据,但是由于另一个线程已经将版本号修改了,导致这次的修改失败,那么应该重新读取数据,再次更新,这就是重试机制。

实现重试机制

使用spring-retry 库

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

引入依赖

<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次

@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+成功次数
@SpringBootTest
public class MpApplicationTests {@Autowiredprivate UserService userService;@Testpublic 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%了,这个视情况而定

http://www.xdnf.cn/news/10728.html

相关文章:

  • 高效易用的 MAC 版 SVN 客户端:macSvn 使用体验
  • 本地部署 Jenkins 并实现外部访问(Windows 版本)
  • PyTorch——线性层及其他层介绍(6)
  • 【HarmonyOS 5】鸿蒙APP使用【团结引擎Unity】开发的案例教程
  • LEAP模型能源需求/供应预测、能源平衡表核算、空气污染物排放预测、碳排放建模预测、成本效益分析、电力系统优化
  • 【macbook】触控板手势
  • 数据解析:一文掌握Python库 lxml 的详细使用(处理XML和HTML的高性能库)
  • 基于 COM 的 XML 解析技术(MSXML) 的总结
  • CSS设置移动端页面底部安全距离
  • 【Hot 100】279. 完全平方数
  • PopupImageMenuItem 无响应
  • AXURE-动态面板
  • 最优包含--字符串dp
  • 解锁技术文档撰写秘籍:从混沌到清晰的蜕变之旅
  • 帝可得 - 策略管理
  • 利用Python 进行自动化操作: Pyautogui 库
  • SQL注入漏洞-上篇
  • 正点原子lwIP协议的学习笔记
  • xmake的简易学习
  • CppCon 2014 学习:Cross platform GUID association with types
  • 蛋白质设计软件LigandMPNN介绍
  • 宇树科技更名“股份有限公司”深度解析:机器人企业IPO前奏与资本化路径
  • R1-Searcher++新突破!强化学习如何赋能大模型动态知识获取?
  • 职坐标IT培训:嵌入式开发C语言/硬件/RTOS路径
  • 时代星光推出战狼W60智能运载无人机,主要性能超市场同类产品一倍!
  • NLP实战(5):基于LSTM的电影评论情感分析模型研究
  • BugKu Web渗透之源代码
  • C++ stl容器之string(字符串类)
  • .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
  • 利用 Scrapy 构建高效网页爬虫:框架解析与实战流程