I would first differentiate between optimistic and pessimistic locks, because they are different in their underlying mechanism.
Optimistic locking is fully controlled by JPA and only requires additional version column in DB tables. It is completely independent of underlying DB engine used to store relational data.
On the other hand, pessimistic locking uses locking mechanism provided by underlying database to lock existing records in tables. JPA needs to know how to trigger these locks and some databases do not support them or only partially.
Now to the list of lock types:
- If entities specify a version field, this is the default. For entities without a version column, using this type of lock isn’t guaranteed to work on any JPA implementation. This mode is usually ignored as stated by ObjectDB. In my opinion it only exists so that you may compute lock mode dynamically and pass it further even if the lock would be OPTIMISTIC in the end. Not very probable usecase though, but it is always good API design to provide an option to reference even the default value.
`LockModeType lockMode = resolveLockMode(); A a = em.find(A.class, 1, lockMode);`
- This is a rarely used option. But it could be reasonable, if you want to lock referencing this entity by another entity. In other words you want to lock working with an entity even if it is not modified, but other entities may be modified in relation to this entity.
- Example: We have entity Book and Shelf. It is possible to add Book to Shelf, but book does not have any reference to its shelf. It is reasonable to lock the action of moving a book to a shelf, so that a book does not end up in another shelf (due to another transaction) before end of this transaction. To lock this action, it is not sufficient to lock current book shelf entity, as the book does not have to be on a shelf yet. It also does not make sense to lock all target bookshelves, as they would be probably different in different transactions. The only thing that makes sense is to lock the book entity itself, even if in our case it does not get changed (it does not hold reference to its bookshelf).
- this mode is similar to
LockModeType.PESSIMISTIC_WRITE, but different in one thing: until write lock is in place on the same entity by some transaction, it should not block reading the entity. It also allows other transactions to lock using
LockModeType.PESSIMISTIC_READ. The differences between WRITE and READ locks are well explained here (ObjectDB) and here (OpenJPA). If an entity is already locked by another transaction, any attempt to lock it will throw an exception. This behavior can be modified to waiting for some time for the lock to be released before throwing an exception and roll back the transaction. In order to do that, specify the
javax.persistence.lock.timeouthint with the number of milliseconds to wait before throwing the exception. There are multiple ways to do this on multiple levels, as described in the Java EE tutorial.
- this is a stronger version of
WRITElock is in place, JPA with the help of the database will prevent any other transaction to read the entity, not only to write as with
- The way how this is implemented in a JPA provider in cooperation with underlying DB is not prescribed. In your case with Oracle, I would say that Oracle does not provide something close to a
SELECT...FOR UPDATEis really rather a
WRITElock. It may be a bug in hibernate or just a decision that, instead of implementing custom “softer”
READlock, the “harder”
WRITElock is used instead. This mostly does not break consistency, but does not hold all rules with
READlocks. You could run some simple tests with
READlocks and long running transactions to find out if more transactions are able to acquire
READlocks on the same entity. This should be possible, whereas not with
- this is another rarely used lock mode. However, it is an option where you need to combine
OPTIMISTICmechanisms. Using plain
PESSIMISTIC_WRITEwould fail in following scenario:
- transaction A uses optimistic locking and reads entity E
- transaction B acquires WRITE lock on entity E
- transaction B commits and releases lock of E
- transaction A updates E and commits
- in step 4, if version column is not incremented by transaction B, nothing prevents A from overwriting changes of B. Lock mode
LockModeType.PESSIMISTIC_FORCE_INCREMENTwill force transaction B to update version number and causing transaction A to fail with
OptimisticLockException, even though B was using pessimistic locking.
- this is the default if entities don’t provide a version field. It means that no locking is enabled conflicts will be resolved on best effort basis and will not be detected. This is the only lock mode allowed outside of a transaction