Bar*_*lom 3 java hibernate jpa transactions race-condition
我有这个交易:
em.getTransaction().begin();
{
final Payment payment = em.find(Payment.class, id);
if (payment.status != Status.INIT)
throw new IllegalStateException("Cannot set to PAID, is not INIT but " + status);
payment.status = Status.PAID;
}
em.getTransaction().commit();
log.info("Payment " + id + " was paid");
Run Code Online (Sandbox Code Playgroud)
但是,正如您在此处所看到的,交易不会阻止竞争条件:
[11:10:18.265] INFO [PaymentServlet] [MSP] Status COMPLETED
[11:10:18.265] INFO [PaymentServlet] Payment c76f9e75-99d7-4721-a8ac-e3a638dd8317 was paid
[11:10:18.267] INFO [PaymentServlet] [MSP] Status COMPLETED
[11:10:18.267] INFO [PaymentServlet] Payment c76f9e75-99d7-4721-a8ac-e3a638dd8317 was paid
Run Code Online (Sandbox Code Playgroud)
付款设置为PAID两次.我没有抛出异常,也没有回滚或任何东西.
我究竟做错了什么?
您需要使用乐观锁定.乐观锁定是很少发生冲突的更新,因此在发生偶然事务时回滚它是可以接受的.悲观锁定会导致数据库在对象使用时保持锁定,有效地对所有内容进行单线程处理并可能导致性能问题.有关更详细的说明,请参见http://en.wikibooks.org/wiki/Java_Persistence/Locking#JPA_2.0_Locking.
要解决此处的问题,您应该向Payment添加一个字段(传统声明是私有Long版本)并为其提供JPA @Version注释.如果要手动管理架构,请确保右表中存在相应的列.然后,JPA将使用此字段检查冲突的更新,并在存在冲突时回滚事务.
更新:有关悲观锁定的更多内容:https://blogs.oracle.com/carolmcdonald/entry/jpa_2_0_concurrency_and简而言之,您可以配置JPA来锁定对象,但这样做是非常罕见的.换句话说,如果您是对JDBC进行手工编码查询,则必须在每次选择结束时写入"for update"以引起悲观锁定; 默认情况下不会锁定读取,因为它会使数据库和数据库用户哭泣.
| 归档时间: |
|
| 查看次数: |
2352 次 |
| 最近记录: |