Locking in JPA
JPA 2 supports both optimistic locking and pessimistic locking. Locking is essential to avoid update collisions resulting from simultaneous updates to the same data by two concurrent users. Locking in ObjectDB (and in JPA) is always at the database object level, i.e. each database object is locked separately.
Optimistic locking is applied on transaction commit. Any database object that has to be updated or deleted is checked. An exception is thrown if it is found out that an update is being performed on an old version of a database object, for which another update has already been committed by another transaction.
When using ObjectDB, optimistic locking is enabled by default and fully automatic. Optimistic locking should be the first choice for most applications, since compared to pessimistic locking it is easier to use and more efficient.
In the rare cases in which update collision must be revealed earlier (before transaction commit) pessimistic locking can be used. When using pessimistic locking, database objects are locked during the transaction and lock conflicts, if they happen, are detected earlier.
This page covers the following topics:
Optimistic Locking
ObjectDB maintains a version number for every entity object. The initial version of a new entity object (when it is stored in the database for the first time) is 1. In every transaction in which an entity object is modified its version number is automatically increased by one. Version numbers are managed internally but can be exposed by defining a version field.
During commit
EntityTransaction.commit() - JPA MethodCommit the current resource transaction, writing any unflushed changes to the database. (and flush
EntityManager.flush() - JPA MethodSynchronize the persistence context to the underlying database.), ObjectDB checks every database object that has to be updated or deleted, and compares the version number of that object in the database to the version number of the in-memory object being updated. The transaction fails and an OptimisticLockException
javax.persistence.OptimisticLockException - JPA ExceptionThrown by the persistence provider when an optimistic locking conflict occurs. is thrown if the version numbers do not match, indicating that the object has been modified by another user (using another EntityManager
javax.persistence.EntityManager - JPA InterfaceInterface used to interact with the persistence context.) since it was retrieved by the current updater.
Optimistic locking is completely automatic and enabled by default in ObjectDB, regardless if a version field (which is required by some ORM JPA providers) is defined in the entity class or not.
Pessimistic Locking
The main supported pessimistic lock modes are:
PESSIMISTIC_READ
javax.persistence.LockModeType.PESSIMISTIC_READ - JPA Enum ConstantPessimistic read lock. - which represents a shared lock.PESSIMISTIC_WRITE
javax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock. - which represents an exclusive lock.
Setting a Pessimistic Lock
An entity object can be locked explicitly by the lock
EntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type. method:
em.lockEntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type.(employee, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..PESSIMISTIC_WRITE
javax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock.);
The first argument is an entity object. The second argument is the requested lock mode.
A TransactionRequiredException
javax.persistence.TransactionRequiredException - JPA ExceptionThrown by the persistence provider when a transaction is required but is not active. is thrown if there is no active transaction when lock
EntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type. is called because explicit locking requires an active transaction.
A LockTimeoutExceptionjavax.persistence.LockTimeoutException - JPA ExceptionThrown by the persistence provider when an pessimistic locking conflict occurs that does not result in transaction rollback. is thrown if the requested pessimistic lock cannot be granted:
- A
PESSIMISTIC_READ
lock request fails if another user (which is represented by anotherEntityManager
instance) currently holds aPESSIMISTIC_WRITE
lock on that database object. - A
PESSIMISTIC_WRITE
lock request fails if another user currently holds either aPESSIMISTIC_WRITE
lock or aPESSIMISTIC_READ lock
on that database object.
For example, consider the following code fragment:
em1.lockEntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type.(e1, lockMode1); em2.lockEntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type.(e2, lockMode2);
em1
and em2
are two EntityManager
instances that manage the same Employee
database object, which is referenced as e1
by em1
and as e2
by em2
(notice that e1
and e2
are two in-memory entity objects that represent one database object).
If both lockMode1
and lockMode2
are PESSIMISTIC_READ
- these lock requests should succeed. Any other combination of pessimistic lock modes, which also includes PESSIMISTIC_WRITE
, will cause a LockTimeoutExceptionjavax.persistence.LockTimeoutException - JPA ExceptionThrown by the persistence provider when an pessimistic locking conflict occurs that does not result in transaction rollback. (on the second lock request).
Pessimistic Lock Timeout
By default, when a pessimistic lock conflict occurs a LockTimeoutException
javax.persistence.LockTimeoutException - JPA ExceptionThrown by the persistence provider when an pessimistic locking conflict occurs that does not result in transaction rollback. is thrown immediately. The "javax.persistence.lock.timeout"
hint can be set to allow waiting for a pessimistic lock for a specified number of milliseconds. The hint can be set in several scopes:
For the entire persistence unit - using a persistence.xml
property:
<properties> <property name="javax.persistence.lock.timeout" value="1000"/> </properties>
For an EntityManagerFactoryjavax.persistence.EntityManagerFactory - JPA InterfaceInterface used to interact with the entity manager factory
for the persistence unit.
- using the createEntityManagerFacotory
Persistence.createEntityManagerFactory(persistenceUnitName,properties) - JPA Static MethodCreate and return an EntityManagerFactory for the named persistence unit using the given properties. method:
Map<String,Object> properties = new HashMap(); properties.put("javax.persistence.lock.timeout", 2000); EntityManagerFactory emf = Persistence.createEntityManagerFactoryPersistence.createEntityManagerFactory(persistenceUnitName,properties) - JPA Static MethodCreate and return an EntityManagerFactory for the named persistence unit using the given properties.("pu", properties);
For an EntityManagerjavax.persistence.EntityManager - JPA InterfaceInterface used to interact with the persistence context.
- using the createEntityManager
EntityManagerFactory.createEntityManager(map) - JPA MethodCreate a new application-managed EntityManager
with the specified Map of properties. method:
Map<String,Object> properties = new HashMap();
properties.put("javax.persistence.lock.timeout", 3000);
EntityManager em = emf.createEntityManagerEntityManagerFactory.createEntityManager(map) - JPA MethodCreate a new application-managed EntityManager
with the
specified Map of properties.(properties);
or using the setProperty
EntityManager.setProperty(propertyName,value) - JPA MethodSet an entity manager property or hint. method:
em.setPropertyEntityManager.setProperty(propertyName,value) - JPA MethodSet an entity manager property or hint.("javax.persistence.lock.timeout", 4000);
In addition, the hint can be set for a specific retrieval operation or query.
Releasing a Pessimistic Lock
Pessimistic locks are automatically released at the end of the transaction (using either commit
EntityTransaction.commit() - JPA MethodCommit the current resource transaction, writing any unflushed changes to the database. or rollback
EntityTransaction.rollback() - JPA MethodRoll back the current resource transaction.).
ObjectDB supports also releasing a lock explicitly while the transaction is active, as so:
em.lockEntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type.(employee, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..NONEjavax.persistence.LockModeType.NONE - JPA Enum ConstantNo lock.);
Other Explicit Lock Modes
In addition to the two main pessimistic modes (PESSIMISTIC_WRITE and
PESSIMISTIC_READ,
which are discussed above), JPA defines additional lock modes that can also be specified as arguments for the lock
EntityManager.lock(entity,lockMode) - JPA MethodLock an entity instance that is contained in the persistence context with the specified lock mode type. method to obtain special effects:
OPTIMISTIC
javax.persistence.LockModeType.OPTIMISTIC - JPA Enum ConstantOptimistic lock. (formerlyREAD
javax.persistence.LockModeType.READ - JPA Enum ConstantSynonymous withOPTIMISTIC
.)OPTIMISTIC_FORCE_INCREMENT
javax.persistence.LockModeType.OPTIMISTIC_FORCE_INCREMENT - JPA Enum ConstantOptimistic lock, with version update. (formerlyWRITE
javax.persistence.LockModeType.WRITE - JPA Enum ConstantSynonymous withOPTIMISTIC_FORCE_INCREMENT
.)PESSIMISTIC_FORCE_INCREMENT
javax.persistence.LockModeType.PESSIMISTIC_FORCE_INCREMENT - JPA Enum ConstantPessimistic write lock, with version update.
Since optimistic locking is applied automatically by ObjectDB to every entity object, the OPTIMISTIC
lock mode has no effect and, if specified, is silently ignored by ObjectDB.
The OPTIMISTIC_FORCE_INCREMENT
mode affects only clean (non dirty) entity objects. Explicit lock at that mode marks the clean entity object as modified (dirty) and increases its version number by 1.
The PESSIMISTIC_FORCE_INCREMENT
mode is equivalent to the PESSIMISTIC_WRITE
mode with the addition that it marks a clean entity object as dirty and increases its version number by one (i.e. it combines PESSIMISTIC_WRITE
with OPTIMISTIC_FORCE_INCREMENT)
.
Locking during Retrieval
JPA 2 provides various methods for locking entity objects when they are retrieved from the database. In addition to improving efficiency (relative to a retrieval followed by a separate lock), these methods perform retrieval and locking as one atomic operation.
For example, the find
EntityManager.find(entityClass,primaryKey,lockMode) - JPA MethodFind by primary key and lock. method has a form that accepts a lock mode:
Employee employee = em.findEntityManager.find(entityClass,primaryKey,lockMode) - JPA MethodFind by primary key and lock.( Employee.class, 1, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..PESSIMISTIC_WRITE
javax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock.);
Similarly, the refresh
EntityManager.refresh(entity,lockMode) - JPA MethodRefresh the state of the instance from the database, overwriting changes made to the entity, if any, and lock it with respect to given lock mode type. method can also receive a lock mode:
em.refreshEntityManager.refresh(entity,lockMode) - JPA MethodRefresh the state of the instance from the database, overwriting changes made to the entity, if any, and lock it with respect to given lock mode type.(employee, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..PESSIMISTIC_WRITE
javax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock.);
A lock mode can also be set for a query in order to lock all the query result objects.
When a retrieval operation includes pessimistic locking, timeout can be specified as a property. For example:
Map<String,Object> properties = new HashMap(); properties.put("javax.persistence.lock.timeout", 2000); Employee employee = em.findjavax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock.( Employee.class, 1, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..PESSIMISTIC_WRITEjavax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock., properties); ... em.refreshjavax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock.(employee, LockModeTypejavax.persistence.LockModeType - JPA EnumLock modes can be specified by means of passing aLockModeType
argument to one of the {@link javax.persistence.EntityManager} methods that take locks (lock
,find
, orrefresh
) or to the {@link Query#setLockMode Query.setLockMode()} or {@link TypedQuery#setLockMode TypedQuery.setLockMode()} method..PESSIMISTIC_WRITEjavax.persistence.LockModeType.PESSIMISTIC_WRITE - JPA Enum ConstantPessimistic write lock., properties);
Setting timeout at the operation level overrides setting in higher scopes.