When before committing the current transaction hibernate checks the version of the row, it should issue an sql select
statement for fetching ithe row.
Assume that after issuing that select
statement hibernate finds out that the row version is not changed, hence it should proceed with committing the transaction.
I'm wondering how hibernate can be sure that in time slot between selecting the row and committing the current transaction no any other transaction will update the row changing its version number ? The only possible thing that hibernate can do seems to be the row version selection with pessimistic locking using Select ... For Update
or a transaction with such an isolation level which will lock the row that is being read.
If what I am thinking is true:
then hibernate optimistic locking does actually use a pessimistic locking for its operation although that pessimistic lock is held for a very short time as the transaction will be committed immediately after that.
otherwise we have a short time slot between row version check and a commit, where a race condition can occur.
Please share your thoughts.
For the default optimistic locking mechanism, the one given by the @Version
annotation, there is no such risk.
Optimistic locking does not require any extra SELECT to get and check the version after the entity was modified. So, there are two steps involved:
The entity is fetched from the DB along with its version:
SELECT * FROM PRODUCT WHERE ID = 1;
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;
So, Hibernate does not check the entity version. The DB checks it using the WHERE clause.
Hibernate only checks the updateCount
result of the PreparedStatement.executeUpdate
method call. If the count is not updateCount
, it means that either the row was deleted or the version has changed, meaning that we are using stale data so an OptimisticLockException
will be thrown.
Therefore, no conflicts can occur for the default @Version
based optimistic locking because a record can only be modified by a single transaction at a time, and once the row is locked by a modification, the lock will be kept until the transaction commits or rolls back.
Only the explicit LockModeType.OPTIMISTIC
can lead to race conditions. However, you can easily fix that using a pessimistic shared or explicit lock.