【发布时间】:2019-08-15 09:49:31
【问题描述】:
我正在使用 Spring retry 来处理在出现意外小错误的情况下重试给定的工作方法。这是我简化的当前代码:
public class WorkerClass {
@Autowired
protected MyJpaRepository myJpaRepository;
@Retryable(
value = {Exception.class}, maxAttempts=3, backoff=@Backoff(5000))
public void doSomething(RandomDTO dto) throws Exception {
boolean success = false;
try {
// perform the task
success = true;
}
catch (Exception e) {
LOGGER.error("Something went wrong");
}
if (!success) {
// create logEntity for failure using DTO
MyEntity entity = myJpaRespository.save(logEntity);
// update DTO using auto generated ID from entity
throw new Exception("Give it another try!");
}
else {
// create logEntity for success using DTO
myJpaRespository.save(logEntity);
}
}
@Recover
public void recover(Exception ex, RandomDTO dto) {
LOGGER.error("fatal error; all retries failed");
// create logEntity for failure using DTO
myJpaRespository.save(logEntity); // EXCEPTION OCCURS HERE
}
}
我观察到的是,doSomething() 方法的初始尝试和所有后续尝试都没有错误地完成。但是,当调用 recover() 方法时,尝试写入存储库时会出现 JPA 异常。
堆栈跟踪包含:
乐观锁定失败;嵌套异常是 org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction(或未保存值映射不正确)
我对这个问题的理解是,由于某种原因,Hibernate 对doSomething() 方法中创建的实体保持锁定。然后,recover() 方法被 Spring 重试框架击中,更新失败,因为其他东西锁定了该记录。但是,我不明白为什么会发生这种情况。有人可以解释一下吗?
【问题讨论】:
-
logEntity 从何而来。它在哪里创建/从数据库中获取?你能添加它的实体类吗?
-
添加代码与问题并不真正相关。实体是使用 DTO(也包含主键 ID 字段)生成的。因此,每当我们需要使用 DTO 编写实体时,我们都会从头开始创建实体对象,然后使用 JPA 存储库保存它。这种方法几乎有效,
recover()方法除外。 -
@Maciej 你知道有什么方法可以告诉 Hibernate/JPA 停止跟踪给定的实体吗?
-
当您的实体上有@Version 字段时,会管理乐观锁定。这就是为什么我对那个实体的定义感兴趣..
-
是的,有一个带有
@Version注解的字段。为什么这在这里很重要?
标签: spring hibernate spring-retry