【发布时间】:2021-05-11 08:36:05
【问题描述】:
我有一个服务方法,我通过 ID 从数据库中请求一个实体。如果实体具有paid == false 属性,我将其设置为true 并做一些事情。如果paid==true 它只是返回。
@Override
@Transactional(rollbackFor={ServiceException.class})
public void handleIntentSucceeded(PaymentIntent intent) throws ServiceException {
LOGGER.trace("handleIntentSucceeded({})", intent);
CreditCharge charge = transactionRepository.findByPaymentIntentId(intent.getId());
if(charge.getPaid()) {
return;
// do some stuff
charge.setPaid(true);
transactionRepository.save(charge);
}
现在如果有多个请求同时具有相同的intent,这个方法将不再一致,因为例如第一个请求接收到charge和paid==false,所以它会“一些事情”,如果第二个请求在第一个请求使用paid==true 保存费用之前到达此方法,即使第一个请求已经这样做,它也会做“一些事情”。这是一个正确的结论吗?
为了确保一次只能处理一个请求,以避免“某些事情”被多次执行,我可以将 Transactional 设置为@Transactional(isolation = Isolation.SERIALIZABLE)。这样,任何请求只有在请求之前提交过 Transactional 时才能处理此方法/事务。
这是最好的方法还是有更好的方法?
【问题讨论】:
-
不要要求最好的方法——它是基于意见的,不会把你带到任何地方。让它发挥作用,看看它是否需要改进,然后改进它——这就是你学习的方式。即使事务可以防止脏读等,事务也不是真正关于线程安全的——看看这里stackoverflow.com/questions/6477574/…。
-
如果你想要更好的答案,你应该问“有没有比这个更好的方法?”。并参考这个问题以更好地理解@Transactional stackoverflow.com/questions/1079114/…的用例
-
定义“一致”。我们不知道您的业务需求是什么。如果你的意思是如果另一个事务知道
Charge.paid同时已经更新,那么你不需要SERIALIZABLE,REPEATABLE_READ就足够了。如果您的意思是一个事务开始应该阻止所有其他事务做同样的事情,那么您需要锁定您的实体以进行更新。
标签: java spring spring-boot jpa spring-data-jpa