spring-data-jpa出现ObjectOptimisticLockingFailureException解决方案

spring-data-jpa出现ObjectOptimisticLockingFailureException解决方案

2020-03-04 17:23:17发布 浏览数:6723
概述:spring-data-jpa出现ObjectOptimisticLockingFailureException解决方案

项目持久层框架使用spring-data-jpa,jpa实现采用hibernate。实体使用乐观锁的方式加锁,也就是添加如下字段。

    @Version

    private Long version;

 

最近发现在日志中偶尔报org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class xxx optimistic locking failed异常。底层异常信息是hibernate抛出的org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction 。排查后发现是jpa并发更新同一个实体引起的。具体流程如下(以实体user1为例)

事务A 事务B 实体版本号version 说明 加载实体user1 加载实体user1 1 这里谁前谁后没关系 修改属性:user1.name=张三 修改属性user1.name=李四 这里谁先修改谁后修改没关系 save(user1) 2 检查版本号发现没变化,修改为2 save(user1)  检查版本号,发现自己的版本号1和实际的版本号2不匹配,判定已经被其他事务修改(事务B),则本次更新失败,抛出如上异常。

 

思考后有如下解决方案:

  1. 不通过jpa的实体更新方式,通过原生sql更新语句进行更新,缺点就是舍弃了jpa的对象管理方式。 通过悲观锁(for update),使事务顺序执行。缺点就是相比乐观锁降低了并发性,也需要写sql或者额外加jpa注解的方式,不够方便。 最后一种也是最终项目采用的方案,通过AOP统一拦截这种异常并进行一定次数的重试,spring官方文档在讲AOP的时候也拿这种方式举例,如下:
@Aspect

@Component

@Order(1)

public class OptimisticLockInterceptor {

 

    @Pointcut("within(com.test.apis..*)")

    public void retryPointCut() {

    }

 

    @Around("retryPointCut()")

    public Object test(ProceedingJoinPoint pjp) throws Throwable {

        for (int i = 0; i <= 4; i++) {

            try {

                return pjp.proceed();

            } catch (OptimisticLockingFailureException ex) {

                if (i > 3) {

                    throw ex;

                }

            }

        }

        return null;

    }

}

其中的order(1)表面此拦截在事务拦截的上层,否则会报错。

不过后来想想这种方式如果在重试模块不支持重试的情况下,就会存在问题。这就要看具体业务是什么样,设计不同的处理方式了。

请先
登录
后评论
0 条评论
暂时没有评论
最新文章
更多