为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

有只小跳蛙 2021-10-13 12:45:09
我正在学习 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);        });    }}所以当我阅读 wallet 时,我得到了旧的价值,但我做了Isolation.REPEATABLE_READ. 数据库中的值当然是错误的。你能解释一下有什么问题吗?谢谢!
查看完整描述

1 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

隔离级别 REPTEABLE_READ 按预期工作。


你可以在这里得到一个很好的解释:


Spring @Transactional - 隔离、传播


但为了澄清,这是发生的事情:


                      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

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


使用阻塞操作的可序列化事务级别以便一一处理(这有性能损失)

实现乐观锁(第二个事务如果同时尝试修改同一个账户会抛出异常)

您可以从这里开始回顾乐观锁:JPA 中的乐观锁是如何工作的?


查看完整回答
反对 回复 2021-10-13
  • 1 回答
  • 0 关注
  • 142 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信