试图理解为什么@transactional(noROllBackFor ....不起作用

时间:2015-05-21 08:17:02

标签: java spring jpa transactions

我有一些看起来像这样的代码:

@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。

有关我在哪里出错的任何建议?

1 个答案:

答案 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的作者。