我有一些看起来像这样的代码:
@Transactional(noRollbackFor = { CannotAcquireLockException.class, LockAcquisitionException.class })
public void deleteSomeData() {
int backOffTime = 0;
int fibonacci = 1;
boolean executed = false;
do {
try {
this.myDAO.deleteTheData();
executed = true;
} catch (RuntimeException rt) {
int newBackoffTime = backOffTime + fibonacci;
fibonacci = backOffTime;
backOffTime = newBackoffTime;
try {
Thread.sleep(backOffTime * 100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
} while (!executed);
}
这是在Spring / JPA环境中运行的。代码由许多Quartz驱动的任务运行,这些任务在不同的时间触发,有时同时触发。因此,如果抛出死锁异常,我试图绕过延迟后重新执行更新代码而发生的数据库(MySQL)死锁。
我一直在尝试各种各样的事情并阅读大量关于交易,死锁等的帖子。
问题是,如果this.myDAO.deleteTheData();
抛出CannotAcquireLockException
,则事务被标记为需要回滚。我正在尝试做的是等待一段时间,然后再次尝试更新。然而,由于回滚状态,第二次尝试失败。所以我试图阻止事务被设置为需要回滚,如果抛出此异常。
我不确定我是否已经把这一切都记在了我的脑海中,这段代码无效。该事务仍被标记为需要回滚。
我可以将循环代码移到调用此方法的代码中,这样就无需担心事务,但这会导致重复执行大量代码,我希望将其保持在接近状态尽可能使用DB。
有关我在哪里出错的任何建议?
答案 0 :(得分:3)
在阅读原始帖子上的答案后,我切换到使用Spring Retry并能够删除所有循环代码。该方法现在看起来像这样:
@Transactional
@ConcurrentDBUpdateRetryPolicy
public void deleteSomeData() {
this.myDAO.deleteTheData();
}
使用自定义注释定义我想要使用的重试策略。您不需要执行自定义注释,但这样做可以集中使用公共重试定义。这是自定义注释:
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Retryable(
include = CannotAcquireLockException.class,
maxAttempts = 10,
backoff = @Backoff(delay = 500, maxDelay = 10000, multiplier = 2, random = true)
)
public @interface ConcurrentDBUpdateRetryPolicy {}
到目前为止,这一直非常顺利。感谢那些提出建议的人,感谢Spring Retry的作者。