Spring @Transactional 一次处理一个方法

Spring @Transactional one transaction of method at a time

所需的解决方案:

每年帐户都会注册到数据库并获得从 1 到 n 的唯一编号。明年完结后账号注册时从1到n重复

我有 table 存储(示例):

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 秒)

我认为这与一笔交易开始然后另一笔交易中断而另一笔交易未完成 gettingsaving 另一 gets 相同 available_number

思考如何解决:

  1. 已阅读有关 IsolationSERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE); 确实会阻止交易与其他交易的交互,但不会阻止两个交易获得相同的交易 available_number。 另请阅读 PropagationREQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) id 的作用是,如果在前一个事务未完成时出现新事务,则先前冻结,然后新事务完成,然后首先解冻并完成。不清楚的是,在我的情况下,第一笔交易可能会获得价值,然后冻结,然后在使用相同的 available_number 完成新交易后,第一笔交易取消费用并以相同的 available_number (或我是我理解错了吗?)
  2. 另一种选择是以某种方式在一次 SQL 调用中转换此方法。要编写 nativeQuery update select,它具有逻辑并执行存储和返回对象(有什么建议吗?)。这样只要 @Transactional 应该够了吧?
  3. 有没有办法将方法 Transactions 存储在堆栈中并一次执行一个(之后关闭第一个事务并打开新事务)?

子问题:有没有办法模拟多个请求 @test 并检查新解决方案是否可行? (新帐户注册仅在明年进行)

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

棘手的想法是 getAndIncreaseCorrectAccountNumber 的逻辑在深层,在同一事务中发生了许多其他 CRUD 逻辑,所以我需要将我的 try{}catch{} 拉到上面整个 @Transaction 顶层(否则主要对象被修改而不是回滚,所以 try{}catch{} 似乎在最上层 @Transaction 之上时工作)

在代码中我做了什么:

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

@Column(name = "version_num") @Version private int version;

  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 重现了错误,后来在我修复后它显示递归是处理问题

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