Cmobilecom JPA 2.2.2 Developer Guide

19 Lock

Locking is used to achieve data integrity and consistency for concurrent transactions. A lock can be optimistic or pessimistic.

Optimistic Lock

A version attribute must be defined for an entity type to use optimistic lock. When updating an entity in database, its version column is verified and then increased if successful. If the version column has been changed by another transaction since the entity was read, an OptimisticLockException will be thrown and transaction will be marked for rollback only. For example,
@Entity
public class Employee {
    @Id
    private String id;

    @Version
    private Integer version;
}
Transaction T1:
    EntityTransaction transaction1 = em1.getTransaction();
    transaction1.begin();
    Employee employeeT1 = em1.find(Employee.class, "123001", LockModeType.OPTIMISTIC, null);
    employeeT1.setName("New Name1");
Transaction T2:
    EntityTransaction transaction2 = em2.getTransaction();
    transaction2.begin();
    Employee employeeT2 = em2.find(Employee.class, "123001", LockModeType.OPTIMISTIC, null);
    employeeT2.setName("New Name2");
Commit transaction T1:
    transaction1.commit();
T1 is successfully committed and the version of the entity is increased by 1.

Now commit transaction T2:

    transaction2.commit();
OptimisticLockException is thrown, and T2 is marked for rollback only since the version of the entity was changed by T1.

Lock mode: OPTIMISTIC_FORCE_INCREMENT is the same as OPTIMISTIC except the version of an entity will be increased even if the entity is not changed.

Pessimistic Lock

A pessimistic lock is a database-level lock that is held until the transaction is completed (rolled back or committed). There are three types of pessimistic locks: For example,

Transaction T1:

    EntityTransaction transaction1 = em1.getTransaction();
    transaction1.begin();
    Employee employeeT1 = em1.find(Employee.class, "123001", LockModeType.PESSIMISTIC_WRITE, null);
Transaction T2:
    EntityTransaction transaction2 = em2.getTransaction();
    transaction2.begin();
    em2.find(Employee.class, "123001", LockModeType.PESSIMISTIC_READ, null);
Transaction T2 will not be able to obtain the read lock on the entity since it is exclusively locked by Transaction T1, and LockTimeoutException or PessimisticLockException will be thrown. If a PessimisticLockException is thrown, the transaction will be marked for rollback only.

Transaction T1 will be successfully committed.

    transaction1.commit();

Lock Timeout and Scope

Lock timeout property (javax.persistence.lock.timeout) specifies the number of milliseconds to wait before lock timeout. Lock scope property (javax.persistence.lock.scope) specifies the scope (NORMAL or EXTENDED) of a pessimistic lock. Lock scope NORMAL means that an entity (including foreign keys) will be locked, and lock scope EXTENDED means that an entity (including foreign keys) and related rows in collection tables and join tables owned by the entity will be locked.

Lock timeout and scope are hints, and they can be passed to any method that takes a lock mode parameter, or annotation/XML mapping that contains a lock-mode element. For example,

EntityManager

    Map<String, Object> properties = new HashMap<>();
    properties.put("javax.persistence.lock.timeout", 2000)
    properties.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
    em.find(Employee.class, "123001", LockModeType.PESSIMISTIC_WRITE, properties);
Query or TypedQuery:
    TypedQuery<Employee> query = em.createQuery("select e from Employee e where id='123001'", Employee.class);
    query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
    query.setHint("javax.persistence.lock.timeout", 2000);
    query.setHint("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
Named query:
<named-query name="jpqlListFullTimeEmployeesBySalary">
    <query>select e from FullTimeEmployee e where e.salary between ?1 and ?2 order by e.salary desc, e.name asc</query>
    <lock-mode>PESSIMISTIC_WRITE</lock-mode>
    <hint name="javax.persistence.lock.timeout" value="2000"></hint>
    <hint name="javax.persistence.lock.scope" value="EXTENDED"></hint>
</named-query>
Create EntityManagerFactory:
    Map<String, Object> properties = new HashMap<>();
    properties.put("javax.persistence.lock.timeout", 2000)
    properties.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
    Persistence.createEntityManagerFactory("pu-name", properties);
META-INF/persistence.xml
<persistence>
    <persistence-unit name="pu-name">
        <properties>
            <property name="javax.persistence.lock.timeout" value="2000"/>
            <property name="javax.persistence.lock.scope" value="EXTENDED"/>
        <properties>
    <persistence-unit>
</persistence>
CacheEntity GraphFrames / No Frames