【问题标题】:Spring JPA could not handle multiple transactions in one thread properlySpring JPA 无法在一个线程中正确处理多个事务
【发布时间】:2018-12-19 04:28:18
【问题描述】:

我有以下代码片段。在我调用BatchSettleService.batchSettleWork(Array.asList([1,2,3])) 之后。我发现账户余额在 DB 中只减少了 1。调试结果是每次accountRepository.findByIdForUpdate(2)返回原始Account,在过去的循环中没有任何改变。我试过Isolation.SERIALIZABLE 级别,但结果是一样的。我使用的数据库是 MySQL 5.7.20 InnoDb Engine。 JPA 实现是 Hibernate。我预计账户余额会减少 3. 我对交易的理解有问题吗?提前谢谢!

@Service
public class BatchSettleService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private WorkSettleService workSettleService;

    public List<WorkSettleResponse> batchSettleWork(List<Long> workIds) {
        List<WorkSettleResponse> results= new ArrayList<>();
        for(Long workId:workIds) {
            try {
                results.add(workSettleService.settleWork(new WorkSettleRequest(workId)));
            } catch (WrappedException e) {
                results.add(new WorkSettleResponse(workId,e.getErrCode(),e.getMessage()));
                logger.error("Settle work failed for {}",workId,e);
            }
        }
        return results;
    }

}


   public class WorkSettleService{

        @Autowired
        private AccountRepository accountRepository;

        @Transactional(rollbackFor= {WorkSettleException.class,RuntimeException.class},propagation=Propagation.REQUIRES_NEW)
        public WorkSettleResponse settleWork(WorkSettleRequest req){
             Account account = accountRepository.findByIdForUpdate(2);
             Integer balance = account .getBalance();
             accountRepository.updateBalanceById(account.getId(),balance-1)
        }
    }


    public interface AccountRepository extends Repository<Account, Integer> {

        public Account findById(Integer id);

        @Lock(LockModeType.PESSIMISTIC_WRITE)
        @Query("select ac from Account a where a.id = ?1")
        public Account findByIdForUpdate(Integer id);

        @Modifying
        @Query("update Account a set a.balance = ?2 where a.id = ?1")
        public int updateBalanceById(Integer id,Integer balance);

}

【问题讨论】:

  • @Gab 是的,我想防止并发读取。但我不清楚 PESSIMISTIC_READ 和 PESSIMISTIC_WRITE 之间的区别,所以我选择了严格的。感谢您的评论
  • PESSIMISTIC_READ 防止并发写入(共享锁)和 PESSIMISTIC_WRITE 也防止并发读取(独占锁)。你的编码方式确实需要排他锁
  • @Query("select ac from Account a where a.id = ?1")中难道没有错别字]
  • 恕我直言,您的期望是正确的
  • @Gab 是的,我现在似乎很清楚区别。幸运的是我做出了正确的选择,哈哈。你能帮我研究一下这个案例吗?

标签: java hibernate transactions spring-data-jpa


【解决方案1】:

这个案子已经解决了。 使用account.setBalance(balance-1) 而不是accountRepository.updateBalanceById(account.getId(),balance-1)。 我不知道为什么@Modifying annoation 不更新 EM 缓存,而 @Query 从 EM 缓存中获取数据,这对于 Spring JPA 实现来说有点策略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-16
    • 2019-03-30
    • 1970-01-01
    • 2019-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多