JPA - Optimistic Locking and Use of @Version annotation

[Updated: Dec 12, 2017, Created: Dec 12, 2017]

Locking database tables/rows is a mechanism to avoid simultaneous updates which may result in unexpected results.

Optimistic Locking is a check which ensures that from the beginning of a transaction to the commit, the target data is not modified by other user. If it does then the current update are discarded, the transaction is rolled back and the current user is informed about that.

In JPA, an entity field or property annotated with @Version is used to perform optimistic locking. The entity is automatically enabled for optimistic locking if it has such field.

Example

The Entity

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private Integer id;
  @Version
  private long version;
  private String name;
  private String department;
    .............
}

@Version field to table column mapping

Following code shows how database column is mapped to the @Version field:

public class CheckGeneratedTableMain {
  public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("example-unit");
      EntityManager em = emf.createEntityManager();
      nativeQuery(em, "SHOW TABLES");
      nativeQuery(em, "SHOW COLUMNS from Employee");
      emf.close();
  }

  public static void nativeQuery(EntityManager em, String s) {
      System.out.printf("---------------------------%n'%s'%n", s);
      Query query = em.createNativeQuery(s);
      List list = query.getResultList();
      for (Object o : list) {
          System.out.println(Arrays.toString((Object[]) o));
      }
  }
}
---------------------------
'SHOW TABLES'
[EMPLOYEE, PUBLIC]
---------------------------
'SHOW COLUMNS from Employee'
[ID, INTEGER(10), NO, PRI, NULL]
[DEPARTMENT, VARCHAR(255), YES, , NULL]
[NAME, VARCHAR(255), YES, , NULL]
[VERSION, BIGINT(19), NO, , NULL]

Persisting and Loading

public class OptimisticLockExample {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEmployee();
          updateEmployeeDepartment("Sales", 1);
          updateEmployeeDepartment("Admin", 1);
      } finally {
          entityManagerFactory.close();
      }
  }

  private static void updateEmployeeDepartment(String department, Object primaryKey) {
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, primaryKey);
      em.getTransaction().begin();
      employee.setDepartment(department);
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee updated: " + employee);
  }

  public static void persistEmployee() {
      Employee employee = new Employee("Joe", "IT");
      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      em.persist(employee);
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee persisted: " + employee);
  }
}
Employee persisted: Employee{id=1, version=0, name='Joe', department='IT'}
Employee updated: Employee{id=1, version=1, name='Joe', department='Sales'}
Employee updated: Employee{id=1, version=2, name='Joe', department='Admin'}

As seen in above output, the version value is increased at the end of each transaction (on commit).

In case if from beginning of a given transaction to the commit, the version number is increased externally by another transaction, the OptimisticLockException is thrown (next example).

Example Project

Dependencies and Technologies Used :

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
  • JDK 1.8
  • Maven 3.3.9

Using @Version annotation Example Select All Download
  • jpa-optimistic-locking-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF

See Also