之前写了一些辅助工作相关的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恢复错误的更多相关文章

  1. Spring Boot 乐观锁加锁失败 - 集成AOP

    Spring Boot with AOP 手头上的项目使用了Spring Boot, 在高并发的情况下,经常出现乐观锁加锁失败的情况(OptimisticLockingFailureException ...

  2. spring boot 中@Autowired注解无法自动注入的错误

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/huihuilovei/article/de ...

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

    基于redis的分布式锁spring-boot starter组件,使得项目拥有分布式锁能力变得异常简单,支持spring boot,和spirng mvc等spring相关项目 快速开始 sprin ...

  4. 2020最新的Spring Boot 分布式锁的具体实现(内附代码)

    前言 面试总是会被问到有没有用过分布式锁.redis 锁,大部分读者平时很少接触到,所以只能很无奈的回答 "没有".本文通过 Spring Boot 整合 redisson 来实现 ...

  5. 如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志 | 修订版

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

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

    在做这个项目的期间,看到一篇很有启发性的文章<程序员你为什么这么累>.对于初级程序员来说,拿到需求,第一反应是用什么技术来尽快的完成任务,这本身并没有问题.但长此以往,不仅被需求的更改搞得 ...

  7. spring boot开发 @autowired注入失败

    @autowired注入失败 会出现如下错误提示: 2018-05-28 08:39:41.857 INFO 8080 --- [ restartedMain] org.hibernate.Versi ...

  8. spring boot下接口调用失败重试方案

    背景: 在项目开发中,有时候会出现接口调用失败,本身调用又是异步的,如果是因为一些网络问题请求超时,总想可以重试几次把任务处理掉. 一些RPC框架,比如dubbo都是有重试机制的,但是并不是每一个项目 ...

  9. Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId

    一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...

随机推荐

  1. 时隔一年再读到the star

    The Star Arthur C. Clarke It is three thousand light-years to the Vatican. Once, I believed that spa ...

  2. dubbox 的各种管理和监管

    dubbo官方自带了dubbo-admin及dubbo-simple/dubbo-monitor-simple二个子项目用于服务治理及服务监控. 一.dubbo-admin的部署 这个比较简单,编译打 ...

  3. [LeetCode] Shortest Distance from All Buildings 建筑物的最短距离

    You want to build a house on an empty land which reaches all buildings in the shortest amount of dis ...

  4. 【原】Spark之机器学习(Python版)(一)——聚类

      kmeans聚类相信大家都已经很熟悉了.在Python里我们用kmeans通常调用Sklearn包(当然自己写也很简单).那么在Spark里能不能也直接使用sklean包呢?目前来说直接使用有点困 ...

  5. c#与JavaScript实现对用户名、密码进行RSA非对称加密

    博主最近手上这个项目呢(就是有上百个万恶的复杂excel需要解析的那个项目,参见博客:http://www.cnblogs.com/csqb-511612371/p/4885930.html),由于是 ...

  6. hql中in关键字的用法

    hql: from " + FoodComment.class.getName() + " f where f.id in :groupIds" 封装的方法: publi ...

  7. Hibernate+EhCache配置二级缓存

    步骤: 第一步:加入ehcache.jar 第二步: 在src目录下新建一个文件,名为:ehcache.xml 第三步:在hibernate配置文件的<session-factory>下配 ...

  8. 轻量级jQuery语法高亮代码高亮插件jQuery Litelighter。

    <!DOCTYPE html><html><head><meta charset="UTF-8" /><title>jQ ...

  9. 篇三:访问JSON静态文件

    背景:在定位的时候带出车牌号的前两位,这里就有一个地址和车牌号前两位的映射关系,这个映射关系起初是通过Ajax在页面加载的时候请求去数据库里面查出来赋给一个变量,然后去操作,但是这个过程通常需要4~7 ...

  10. 关于若干php 表单 的重新审视

    对于表单验证来说 应该在任何可能的时候对用户输入进行验证(通过客户端脚本).浏览器验证速度更快,并且可以减轻服务器的负载. 如果用户输入需要插入数据库,您应该考虑使用服务器验证.在服务器验证表单的一种 ...