Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误
之前写了一些辅助工作相关的Spring Boot怎么使用AOP。这里继续正题,怎么减少Spring Boot 乐观锁加锁报错的情况(基本可以解决)。
1. 包依赖
spring-boot-starter-data-jpa, Spring Boot的JPA starter
h2, H2内存数据库
spring-boot-starter-test,Spring Boot的Junit测试starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.2.6.RELEASE</version>
</dependency> <dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.188</version>
<scope>runtime</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.2.6.RELEASE</version>
<scope>test</scope>
</dependency>
2. 如何在启用乐观锁?
我用的是JPA, 所以很简单,在实体类加一个字段,并注解@Version。
@Entity
public class Account { //primary key, auto generated
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id; private String name; // enable optimistic locking version control
@Version
private int version; /*omitted getter/setter, but required*/
}
3. 通过AOP实现对RetryOnOptimisticLockingFailureException的恢复
为了减少对代码的侵入,对之前的AOP例子进行少许修改:
- 自定义一个注解,用来标注需要恢复这个错误的接口
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOnOptimisticLockingFailure { }
- 切入点表达式使用注解,不再使用execution
@Pointcut("@annotation(RetryOnOptimisticLockingFailure)")
public void retryOnOptFailure() {
// pointcut mark
}
@Around("retryOnOptFailure()")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
do {
numAttempts++;
try {
return pjp.proceed();
} catch (OptimisticLockingFailureException ex) {
if (numAttempts > maxRetries) {
//log failure information, and throw exception
throw ex;
}else{
//log failure information for audit/reference
//will try recovery
}
}
} while (numAttempts <= this.maxRetries);
return null;
}
- 在需要对错误进行恢复的RESTFul接口加上恢复标签
至于为什么一定是要在RESTFul接口上加,而不是其他地方(例如service层),是因为Spring Boot的事务管理的上下文是从resource层开始建立的,在service层恢复是无效的,因为数据库的操作依然是在之前失败的事务里,之后再细说吧。
@RestController
@RequestMapping("/account")
public class AccountResource { @Autowired
private AccountService accountService; @RequestMapping(value = "/{id}/{name}", method = RequestMethod.PUT)
@ResponseBody
@RetryOnOptimisticLockingFailure
public void updateName(@PathVariable Integer id, @PathVariable String name) {
accountService.updateName(id, name);
}
}
4. 测试用例
@Test
public void testUpdate() {
new Thread(() -> this.client.put(base + "/1/llt-2", null)).start();
new Thread(() -> this.client.put(base + "/1/llt-3", null)).start(); try {
//waiting for execution result of service
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
5. 测试一下效果如何
- 没有在AccountResource的updateName方法加@RetryOnOptimisticLockingFailure:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.leolztang.sb.aop.model.Account] with identifier [1]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.leolztang.sb.aop.model.Account#1]] with root cause org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.leolztang.sb.aop.model.Account#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285)
- 在AccountResource的updateName方法加@RetryOnOptimisticLockingFailure:
Original:name=[llz-1],version=[0],New:name=[llt-2],version=[1]
Original:name=[llt-2],version=[1],New:name=[llt-3],version=[2]
6. 完整代码
http://files.cnblogs.com/files/leolztang/sb.aop-v2.tar.gz
Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误的更多相关文章
- Spring Boot 乐观锁加锁失败 - 集成AOP
Spring Boot with AOP 手头上的项目使用了Spring Boot, 在高并发的情况下,经常出现乐观锁加锁失败的情况(OptimisticLockingFailureException ...
- spring boot 中@Autowired注解无法自动注入的错误
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/huihuilovei/article/de ...
- spring boot 分布式锁组件 spring-boot-klock-starter
基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目 快速开始 sprin ...
- 2020最新的Spring Boot 分布式锁的具体实现(内附代码)
前言 面试总是会被问到有没有用过分布式锁.redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 "没有".本文通过 Spring Boot 整合 redisson 来实现 ...
- 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版
欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...
- spring boot 从开发到上线(三)—AOP 异常监控、上报
在做这个项目的期间,看到一篇很有启发性的文章<程序员你为什么这么累>.对于初级程序员来说,拿到需求,第一反应是用什么技术来尽快的完成任务,这本身并没有问题.但长此以往,不仅被需求的更改搞得 ...
- spring boot开发 @autowired注入失败
@autowired注入失败 会出现如下错误提示: 2018-05-28 08:39:41.857 INFO 8080 --- [ restartedMain] org.hibernate.Versi ...
- spring boot下接口调用失败重试方案
背景: 在项目开发中,有时候会出现接口调用失败,本身调用又是异步的,如果是因为一些网络问题请求超时,总想可以重试几次把任务处理掉. 一些RPC框架,比如dubbo都是有重试机制的,但是并不是每一个项目 ...
- Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId
一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...
随机推荐
- FastCgi与PHP-fpm关系
1 CGI (1)什么是CGI: CGI(Common Gateway Interface)公共网关接口, 是WWW技术中最重要的技术之一,有着不可替代的重要地位, CGI是外部应用程序(CGI程序 ...
- SQL操作符
Oracle中的操作符算术操作符:无论是在sqlserver,或者是java中,每种语言它都有算术操作符,大同小异. Oracle中算术操作符(+)(-)(*)(/) 值得注意的是:/ 在oracle ...
- linux内存管理
一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分: 1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...
- [LeetCode] Max Points on a Line 共线点个数
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. ...
- 火狐下js的日期比较的方法
function tab(date1,date2){ var oDate1 = new Date(date1); var oDate2 = new Date(date2); if(oDate1.get ...
- FCM聚类算法介绍
FCM算法是一种基于划分的聚类算法,它的思想就是使得被划分到同一簇的对象之间相似度最大,而不同簇之间的相似度最小.模糊C均值算法是普通C均值算法的改进,普通C均值算法对于数据的划分是硬性的,而FCM则 ...
- HttpURLConnection类
导语 java.net.HttpURLConnectin类是URLConnection类的抽象子类.它在处理协议为HTTP的URL时特别有效.具体而言,它通过它可以获取和设置请求方法,确定是否重定向, ...
- C 语言学习 第12次作业总结
作业总结 本次课堂的内容为字符串相关的几个函数还有结构体. 字符串相关函数 在此之前的课程中,输入主要都是使用scanf这个函数.而在这节课上,冯老师讲解了字符串获取函数gets.在不需要控制符的情况 ...
- 使用TortoiseGit对android studio工程进行代码版本控制
阅读下列文章时请保证你是否有以下工具: 1.Android Studio 2.TortoiseGit 和 msysGit 安卓工程版本控制哪些文件可以忽略 可以省略的文件如下: 目录 .gradle ...
- 【20160722-20160728】NOI2016滚粗记&&酱油记&&游记
先挖坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs.c ...