Spring @Transactional一次进行一次方法交易

时间:2018-10-25 13:09:13

标签: spring multithreading hibernate transactional

必需的解决方案:

每年的帐户注册到数据库,并获得从1到n的唯一编号。一年结束后,明年将在帐户注册时从1到n成为中继器。

我有一个存储表(示例)的表:

id     , available_number, current_year
someId0, 8000            , 2000
someId1, 2500            , 2001

方法片段:

@Transactional
public AccountNumber getAndIncreaseCorrectAccountNumber(String current_year) {
    AccountNumber accountNumber;
    Optional<AccountNumber> foundAccountNumber = Optional.ofNullable(AccountNumberRepository.findByYear(current_year));
    if(!foundAccountNumber .isPresent()) {
        accountNumber = new AccountNumber();
        accountNumber .setAvailableNumber(1L);
        accountNumber .setCurrentYear(current_year);
    } else {
        accountNumber = foundAccountNumber.get();
        accountNumber.setAvailableNumber(accountNumber.getAvailableNumber() + 1);
    }
    return accountRepository.save(accountNumber);
}

问题:

当多个帐户在最后一天(最后一分钟)注册时,帐户将获得相同的available_number。注意到有4个帐户在1秒内用相同的available_number注册(两次注册之间相差约0.1秒)

我认为这与一件交易开始然后另一笔交易中断,而另一笔交易未完成getting和另一笔saving相同的{{1} }

思考如何解决:

  1. 已了解getsavailable_number确实阻止了事务与其他事务的交互,但是并没有阻止两个事务获得相同的Isolation。 另请了解SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);available_number的ID含义,如果新事务出现而前一个事务尚未完成,则先前的冻结,然后新事务完成,然后新的冻结和完成。尚不清楚的是,在我的情况下,第一笔交易可能会获得价值,然后冻结,然后在使用相同的Propagation完成新交易之后,第一笔交易便会松懈并以相同的REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW)完成(或者是我在这里误会了?)
  2. 另一种选择是以某种方式在一个SQL调用中转换此方法。要编写available_number,请选择要更新的逻辑,并存储和返回对象(有什么建议吗?)。这样仅available_number就足够了吗?
  3. 是否有一种方法可以将方法nativeQuery存储在堆栈中并一次执行一次(在此之后,关闭第一个事务并打开新事务)?

子问题:是否有一种方法可以模拟多个请求@Transactional并检查何时新解决方案可能起作用? (新帐户注册仅在明年进行)

1 个答案:

答案 0 :(得分:0)

正如@JBNizet所写,而我在其他文章中也读过,OptimisticLock解决了该问题,因为它只允许修改该行的一个事务成功完成。 其他事务将与ObjectOptimisticLockingFailureException一起抛出。 然后,我使用try{}catch{}ObjectOptimisticLockingFailureException进行了递归,以重定向回相同的方法

Tricky认为getAndIncreaseCorrectAccountNumber的逻辑是深层的,在同一事务中发生了许多其他CRUD的逻辑,因此我需要将try{}catch{}置于整体之上@Transaction顶层(否则主要对象已被修改,而不是回滚,因此,try{}catch{}在最高@Transaction上方时似乎可以工作)

在代码中我做了什么:

  1. AccountNumber中添加了OptimisticLock行的PostgreSQL版本
  

@Column(name =“ version_num”)@Version私有int版本;

  1. 在存储层AccountNumberRepository中,在方法上方添加了@Lock(LockModeType.OPTIMISTIC)

  2. 添加了try{}catch{}来捕获OptimisticLock并启用对同一方法的递归

public String submitSomeObjectWithRecursionHandling(@NotNull SomeObject someObject, @NotBlank String accountId){
        try {
        return submitSomeObject(someObject, accountId);
        } catch (ObjectOptimisticLockingFailureException | OptimisticLockException e) {
            e.printStackTrace();
            return submitSomeObjectWithRecursionHandling(someObject, accountId);
        }
    }

submitSomeObject(someObject, accountId)开始了@Transaction

我没有设法实现的是编写集成测试。 因此,我创建了10个准备提交的SomeObects,并从前端立即发送要提交的请求,它首先复制了accountNumber,然后重现了该错误,后来在修复后,表明递归是处理问题

如果您有一些方法可以在集成测试中测试此解决方案,请告诉我。