Hibernate在提交事务之前如何进行乐观锁的行版本检查?

5
在提交当前事务之前,Hibernate会检查行的版本,它应该发出一个SQL select语句来获取该行。假设在发出该 select 语句后,Hibernate发现该行版本未更改,因此应该继续提交事务。
我想知道Hibernate如何确保在选择该行和提交当前事务之间的时间段内,没有任何其他事务会更新该行并更改其版本号?Hibernate唯一可能做的事情似乎是使用悲观锁定对行版本进行选择,使用Select ... For Update或具有锁定正在读取的行的隔离级别的事务。
如果我的想法是正确的:
  • 那么Hibernate乐观锁定实际上确实使用了悲观锁定作为其操作,尽管该悲观锁定的持续时间非常短,因为事务将立即在其后提交。

  • 否则,在行版本检查和提交之间存在一个短时间间隔,可能会发生竞争条件。

请分享您的想法。
1个回答

9
对于默认的乐观锁机制,由@Version注释提供,不存在这种风险。
乐观锁不需要任何额外的SELECT来获取和检查实体在修改后的版本。因此,涉及两个步骤:
  1. The entity is fetched from the DB along with its version:

     SELECT * FROM PRODUCT WHERE ID = 1;
    
  2. The UPDATE or DELETE will use the version fetched by the same SELECT that fetched the entity:

     UPDATE PRODUCT SET (LIKES, QUANTITY, VERSION) = (5, 10, 3) 
     WHERE ID = 1 AND VERSION = 2;
    
因此,Hibernate不检查实体版本。数据库使用WHERE子句进行检查。
Hibernate仅检查PreparedStatement.executeUpdate方法调用的updateCount结果。如果计数不是updateCount,则意味着行已被删除或版本已更改,这意味着我们正在使用陈旧的数据,因此会抛出OptimisticLockException异常。
因此,默认情况下基于@Version的乐观锁定不会发生冲突,因为记录一次只能由一个事务修改,一旦行被修改锁定,锁将保持直到事务提交或回滚。

只有显式使用 LockModeType.OPTIMISTIC 才会导致竞态条件。但是,您可以轻松 使用悲观共享或显式锁定来解决这个问题


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接