当hibernate在提交当前事务之前检查行的版本时,它应该发出一条sqlselect
语句来获取该行。
假设在发出select
语句后hibernate发现行版本没有更改,因此它应该继续提交事务。
我想知道hibernate如何确保在选择行和提交当前事务之间的时间段内,没有任何其他事务会更新行并更改其版本号?hibernate唯一可能做的事情似乎是使用Select ... For Update
进行悲观锁定的行版本选择,或者使用这样一个隔离级别的事务来锁定正在读取的行。
如果我的想法是真的:
-
则hibernate乐观锁定实际上使用悲观锁定进行操作,尽管该悲观锁定保持的时间很短,因为之后将立即提交事务。
-
否则,在行版本检查和提交之间的时间间隔很短,可能会出现竞争条件。
请分享您的想法。
对于默认的乐观锁定机制,即@Version
注释给出的机制,不存在这样的风险。
乐观锁定不需要任何额外的SELECT来获取和检查实体修改后的版本。因此,有两个步骤:
-
从数据库中提取实体及其版本:
SELECT * FROM PRODUCT WHERE ID = 1;
-
UPDATE或DELETE将使用由获取实体的SELECT获取的版本:
UPDATE PRODUCT SET (LIKES, QUANTITY, VERSION) = (5, 10, 3) WHERE ID = 1 AND VERSION = 2;
因此,Hibernate不会检查实体版本。DB使用WHERE子句进行检查。
Hibernate只检查PreparedStatement.executeUpdate
方法调用的updateCount
结果。如果计数不是updateCount
,则意味着行被删除或版本发生了更改,这意味着我们使用的是过时的数据,因此将抛出OptimisticLockException
。
因此,默认的基于@Version
的乐观锁定不会发生冲突,因为一次只能由单个事务修改记录,并且一旦通过修改锁定了行,该锁定将一直保持到事务提交或回滚。
只有明确的LockModeType.OPTIMISTIC
才能导致竞争条件。但是,使用悲观共享锁或显式锁可以很容易地解决这个问题。