zoukankan      html  css  js  c++  java
  • Spring Boot 乐观锁加锁失败

    之前写了一些辅助工作相关的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

  • 相关阅读:
    Java数据结构和算法(三)——冒泡、选择、插入排序算法
    Java数据结构和算法(二)——数组
    Java数据结构和算法(一)——简介
    Linux系列教程(二十四)——Linux的系统管理
    部署python项目到linux服务器
    ImportError:No module named 'PIL'
    Django异常
    linux安装mysqlclient报错
    python升级带来的yum异常:File "/usr/bin/yum", line 30
    python3+django+mysql
  • 原文地址:https://www.cnblogs.com/leolztang/p/5450316.html
Copyright © 2011-2022 走看看