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
 1         <dependency>
 2             <groupId>org.springframework.boot</groupId>
 3             <artifactId>spring-boot-starter-data-jpa</artifactId>
 4             <version>1.2.6.RELEASE</version>
 5         </dependency>
 6
 7         <dependency>
 8             <groupId>com.h2database</groupId>
 9             <artifactId>h2</artifactId>
10             <version>1.4.188</version>
11             <scope>runtime</scope>
12         </dependency>
13
14         <dependency>
15             <groupId>org.springframework.boot</groupId>
16             <artifactId>spring-boot-starter-test</artifactId>
17             <version>1.2.6.RELEASE</version>
18             <scope>test</scope>
19         </dependency>

2. 如何在启用乐观锁?

我用的是JPA, 所以很简单,在实体类加一个字段,并注解@Version。

 1 @Entity
 2 public class Account {
 3
 4        //primary key, auto generated
 5     @Id
 6     @GeneratedValue(strategy = GenerationType.AUTO)
 7     private int id;
 8
 9     private String name;
10
11     // enable optimistic locking version control
12     @Version
13     private int version;
14
15 /*omitted getter/setter, but required*/
16 }

3. 通过AOP实现对RetryOnOptimisticLockingFailureException的恢复

为了减少对代码的侵入,对之前的AOP例子进行少许修改:

  • 自定义一个注解,用来标注需要恢复这个错误的接口
1 @Retention(RetentionPolicy.RUNTIME)
2 public @interface RetryOnOptimisticLockingFailure {
3
4 }
  • 切入点表达式使用注解,不再使用execution
 1     @Pointcut("@annotation(RetryOnOptimisticLockingFailure)")
 2     public void retryOnOptFailure() {
 3         // pointcut mark
 4     }
 5   
 6     @Around("retryOnOptFailure()")
 7     public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
 8         int numAttempts = 0;
 9         do {
10             numAttempts++;
11             try {
12                 return pjp.proceed();
13             } catch (OptimisticLockingFailureException ex) {
14                 if (numAttempts > maxRetries) {
15                     //log failure information, and throw exception
16                     throw ex;
17                 }else{
18                     //log failure information for audit/reference
19                     //will try recovery
20                 }
21             }
22         } while (numAttempts <= this.maxRetries);
23
24         return null;
25     }
  • 在需要对错误进行恢复的RESTFul接口加上恢复标签

至于为什么一定是要在RESTFul接口上加,而不是其他地方(例如service层),是因为Spring Boot的事务管理的上下文是从resource层开始建立的,在service层恢复是无效的,因为数据库的操作依然是在之前失败的事务里,之后再细说吧。

 1 @RestController
 2 @RequestMapping("/account")
 3 public class AccountResource {
 4
 5     @Autowired
 6     private AccountService accountService;
 7
 8     @RequestMapping(value = "/{id}/{name}", method = RequestMethod.PUT)
 9     @ResponseBody
10     @RetryOnOptimisticLockingFailure
11     public void updateName(@PathVariable Integer id, @PathVariable String name) {
12         accountService.updateName(id, name);
13     }
14 }

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

时间: 2024-10-06 20:43:03

Spring Boot 乐观锁加锁失败 - 使用AOP恢复错误的相关文章

spring boot 分布式锁组件 spring-boot-klock-starter

基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目 快速开始 spring boot项目接入 1.添加lock starter组件依赖,目前还没上传到公共仓库,需要自己下源码build ,已上传到maven中央仓库 <dependency> <groupId>org.springframework.boot</groupId> <arti

spring boot 从开发到上线(三)—AOP 异常监控、上报

在做这个项目的期间,看到一篇很有启发性的文章<程序员你为什么这么累>.对于初级程序员来说,拿到需求,第一反应是用什么技术来尽快的完成任务,这本身并没有问题.但长此以往,不仅被需求的更改搞得疲惫不堪,更被重复的工作消磨了激情.如果你也有类似的烦恼,不妨看看此文,结合日常工作,体会下文中提到的先有思想再有技术. ~~~~~~~~ge ge ge ge~~~~~~~~~~~ 正文: 到目前为止,我们的项目线上运行良好,但每个人都知道,它一定有 bug.当异常产生,就需要及时去修复.然而我们不可能实时

Spring Boot 2.0下配置Log4j2下的错误问题分析与解决

环境介绍 Spring Boot 2.0.2 Java 8 任务描述 由于Spring Boot 2.0 默认情况下是使用logback作为日志系统的,这里希望切换到log4j2. pom.xml内容定义 这里在pom.xml新增了spring-boot中的日志组件 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</

【Todo】秒杀系统 &amp; 乐观锁 &amp; Nginx反向代理

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC" } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC"; min-height: 17.0px } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; line-height: 16.0px; font: 14.0px &qu

【mysql】关于乐观锁

一.乐观锁介绍 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检,乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁.类似SVN 悲观锁假定其他用户企图访问或者改变你正在访问.更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁.

乐观锁与悲观锁的应用场景

锁( locking ) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个 cut-off 时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种,也可能是几个小时),数据再发生变化.此时,我们就需要通过一些机 制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓 的 " 锁 " ,即给我们选定的目标数据上锁,使其无法被其他程序修改. Hibernate 支持两种锁机制:即通常所说的 " 悲

【Java多线程】悲观锁 与 乐观锁

一:悲观锁 悲观锁,就是不管是否发生多线程冲突,只要存在这种可能,就每次访问都加锁,加锁就会导致锁之间的争夺,有争夺就会有输赢,输者等待. syncrhoized是一种独占锁,即:占用该锁的线程才可以执行,申请该锁的线程就只能挂起等待,直到占用锁的线程释放锁才唤醒,拿到锁并执行.由于在进程挂起和恢复执行过程中存在着很大的开销,并且当一个线程正在等待锁时,它不能做任何事.所以syncrhoized是一种悲观锁,凡是用syncrhoized加了锁的多线程之间都会因锁的争夺结果导致挂起.唤醒等开销.

web开发中的两把锁之数据库锁:(高并发--乐观锁、悲观锁)

这篇文章讲了 1.同步异步概念(消去很多疑惑),同步就是一件事一件事的做:sychronized就是保证线程一个一个的执行. 2.我们需要明白,锁机制有两个层面,一种是代码层次上的,如Java中的同步锁,典型的就是同步关键字synchronized ( 线    程级别的).另一个就是数据库层次上的,比较典型的就是悲观锁和乐观锁. 3.常见并发同步案例分析   附原文链接 http://www.cnblogs.com/xiohao/p/4385508.html 对于我们开发的网站,如果网站的访问

Java中乐观锁与悲观锁的实现

锁(locking) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种,也可能是几个小时),数据再发生变化.此时,我们就需要通过一些机 制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓 的“锁”,即给我们选定的目标数据上锁,使其无法被其他程序修改. Hibernate支持两种锁机制:即通常所说的“悲观锁(Pessimistic Lockin