春季JPA。更新数据库值的正确方法

时间:2018-09-26 13:42:29

标签: java database spring spring-data-jpa isolation-level

我正在学习Spring JPA和Hibernate。所以我遇到了一个问题。

我有此方法

@Transactional(isolation = Isolation.REPEATABLE_READ)
public void sendMoney(Long from, Long to, Double amount) {
    WalletEntity fromWallet = walletServiceImpl.getWallet(from);
    WalletEntity toWallet = walletServiceImpl.getWallet(to);
    fromWallet.setAmount(fromWallet.getAmount() - amount);
    toWallet.setAmount(toWallet.getAmount() + amount);

    TransactionEntity transaction = new TransactionEntity();
    transaction.setAmount(amount);
    transaction.setToWallet(toWallet);
    transaction.setFromWallet(fromWallet);

    transactionRepository.saveAndFlush(transaction);
}

我想对其进行测试并创建它:

@GetMapping("/send")
public void sendMoney() {
    ExecutorService executorService = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 100; i++) {
        executorService.execute(() -> {
            accountServiceImpl.sendMoney(1L, 2L, 10D);
        });
    }
}

因此,当我阅读钱包时,我得到了旧值,但我赚了Isolation.REPEATABLE_READ。数据库中的值当然是错误的。 你能解释出什么问题吗?谢谢!

1 个答案:

答案 0 :(得分:0)

隔离级别REPTEABLE_READ按预期工作。

您可以在这里得到很好的解释:

Spring @Transactional - isolation, propagation

但是为了澄清,这是发生了什么:

                      Tx1      Tx2
                       |        |
Tx1 Read Wallet 1     100       |
Tx2 Read Wallet 1      |       100
Tx1 Discounts 10     100-10     |
Tx2 Discounts 10       |      100-10
Tx1 Commits            |        |
Tx2 Commits            |        |
Tx1 Read Wallet 1      90       |
Tx2 Read Wallet 2      |        90

因此,为了控制此行为,您有两个选择:

  1. 使用阻塞操作的可序列化事务级别来逐个处理(这会降低性能)
  2. 实施乐观锁定(如果第二笔交易尝试同时修改同一帐户,则会引发异常)

您可以在此处开始查看乐观锁定:Optimistic locking in JPA how does it work?