项目持久层框架使用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),则本次更新失败,抛出如上异常。
|
思考后有如下解决方案:
-
不通过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)表面此拦截在事务拦截的上层,否则会报错。
不过后来想想这种方式如果在重试模块不支持重试的情况下,就会存在问题。这就要看具体业务是什么样,设计不同的处理方式了。
项目持久层框架使用spring-data-jpa,jpa实现采用hibernate。实体使用乐观锁的方式加锁,也就是添加如下字段。 @Version private Long version;最近发现在日志中偶尔报org.springframework.orm.ObjectOptimisticLockingFailureException: Object of cla...
一、问题描述
新开发的系统,往往可能需要将旧版的系统中的历史数据,用脚本的方式在新系统中跑一遍业务流程,其实可能是用Java代码自动调用一些业务流程接口。
在执行过程中发现报错:
2021-01-27 19:32:46.300 [http-nio-5090-exec-4] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet]:182 - Servlet.service() for servlet [dispatcherServlet] in context with path
主要解决方案是在捕捉异常的时候使用递归方法对乐观锁重试
spamMessageFeeReceipt=genSpamMessageFeeReceipt(jsonObject,distribute);
}catch (Exception e){
if(e instanceof ObjectOptimisticLockingFailureException){
spamMessageFeeReceipt=genSpamMessageFeeReceipt(jsonObjec
optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was u...
原因:就是要修改的实体的version和数据库中的version对不上。hibernate就会认为别人已经并发修改了数据。
解决办法:将数据库中的version从前端回传回来。如果version和数据库一致,还是经常出现这个问题,可以考虑使用悲观锁。
乐观锁:给数据加一个版本, 每一操作数据就更新版本,不会上锁,但是在更新的时候你会判断这期间有没有人去更新这个数据
悲观锁:给数据加了一把锁 ,同事务只能一个线程进行操作,使用完了锁释放, 没释放前后面想要操作的人就得排队 ,效率低,但是很安全
2 问题描述
主要解决方案是在捕捉异常的时候使用递归方法对乐观锁重试
public void optimisticLockingFailureSave(User user, RefundLog refundLog, OrderLog order) {
try {
if (user != null) { //更新前要version要和数...
我的博文中,有一篇短文Java结合Junit做并发测试用例,是介绍JPA在高并发下,多个Service方法更新
同一个记录;在异常信息中有StaleObjectStateException和ObjectOptimisticLockingFailureException异常信
息。有些业务这种靠JPA维护的版本信息是可行的,如果两个不同用户同事(高并发)购买一件马丁靴鞋...
spring-boot-starter-data-jpa标红通常是由于以下几个原因导致的:
1. 缺少依赖:确保在项目的pom.xml文件中添加了
spring-boot-starter-data-jpa的依赖项,并且版本号正确。如果依赖项未正确添加或版本号错误,IDE可能会显示标红。
2. 项目配置错误:检查项目的配置文件(如application.properties或application.yml)是否正确设置了与
spring-boot-starter-data-jpa相关的配置项。确保
数据库连接信息、实体类扫描路径等配置正确无误。
3. 编译错误:如果在代码中存在语法错误或其他编译错误,IDE会显示标红。请检查代码中是否存在错误,并进行相应的修正。
4. 依赖冲突:如果项目中存在多个不兼容的依赖项或版本冲突,可能会导致标红。请检查项目的依赖项,并确保它们之间的兼容性和版本一致性。
请根据上述原因逐一排查,找出导致标红的具体原因并进行相应的修正,以解决
spring-boot-starter-data-jpa标红的问题。
remote: You are not allowed to download code from this project. 13fatal: unable to access ‘http://gi
postgresql发生死锁,org.postgresql.util.PSQLException: ERROR: deadlock detected ,Process 17662 waits for
remote: You are not allowed to download code from this project. 13fatal: unable to access ‘http://gi
使用antd日期组件的时候报错Invalid hook call