必需的解决方案:
每年的帐户注册到数据库,并获得从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} }
思考如何解决:
gets
和available_number
确实阻止了事务与其他事务的交互,但是并没有阻止两个事务获得相同的Isolation
。
另请了解SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
和available_number
的ID含义,如果新事务出现而前一个事务尚未完成,则先前的冻结,然后新事务完成,然后新的冻结和完成。尚不清楚的是,在我的情况下,第一笔交易可能会获得价值,然后冻结,然后在使用相同的Propagation
完成新交易之后,第一笔交易便会松懈并以相同的REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW)
完成(或者是我在这里误会了?)available_number
,请选择要更新的逻辑,并存储和返回对象(有什么建议吗?)。这样仅available_number
就足够了吗?nativeQuery
存储在堆栈中并一次执行一次(在此之后,关闭第一个事务并打开新事务)?子问题:是否有一种方法可以模拟多个请求@Transactional
并检查何时新解决方案可能起作用? (新帐户注册仅在明年进行)
答案 0 :(得分:0)
正如@JBNizet所写,而我在其他文章中也读过,OptimisticLock解决了该问题,因为它只允许修改该行的一个事务成功完成。
其他事务将与ObjectOptimisticLockingFailureException
一起抛出。
然后,我使用try{}catch{}
对ObjectOptimisticLockingFailureException
进行了递归,以重定向回相同的方法
Tricky认为getAndIncreaseCorrectAccountNumber
的逻辑是深层的,在同一事务中发生了许多其他CRUD
的逻辑,因此我需要将try{}catch{}
置于整体之上@Transaction
顶层(否则主要对象已被修改,而不是回滚,因此,try{}catch{}
在最高@Transaction
上方时似乎可以工作)
在代码中我做了什么:
AccountNumber
中添加了OptimisticLock行的PostgreSQL版本@Column(name =“ version_num”)@Version私有int版本;
在存储层AccountNumberRepository
中,在方法上方添加了@Lock(LockModeType.OPTIMISTIC)
添加了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
,然后重现了该错误,后来在修复后,表明递归是处理问题
如果您有一些方法可以在集成测试中测试此解决方案,请告诉我。