Close

JPA - Updating Entity Relationship and Version Attribute

[Last Updated: Dec 18, 2017]

Following example shows how version attribute is affected while updating an entity relationships.

Example

The Entity

@Entity
public class Employee {
  @Id
  @GeneratedValue
  private long id;
  @Version
  private long version;
  private String name;
  private String department;
  @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  private List<Phone> phones;
    .............
}
@Entity
public class Phone {
  @Id
  @GeneratedValue
  private int id;
  @Version
  private long version;
  private String number;
  private String type;
    .............
}

Updating Relationship

We are going to modify existing phone number of a particular employee.

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

  public static void main(String[] args) {
      try {
          persistEmployee();
          employeePhoneUpdate1();
          employeePhoneUpdate2();
          loadEmployee();
      } finally {
          entityManagerFactory.close();
      }
  }

  private static void employeePhoneUpdate1() {
      System.out.println("Update 1 starts, changing employee existing phone number");
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, 1L);
      em.getTransaction().begin();
      employee.getPhones().get(0).setNumber("111-222-3333");
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee updated 1: " + employee);
  }

  private static void employeePhoneUpdate2() {
      System.out.println("Update 2 starts, changing employee existing phone number again");
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, 1L);
      em.getTransaction().begin();
      employee.getPhones().get(0).setNumber("222-333-3333");
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee updated 2: " + employee);
  }

  private static void loadEmployee() {
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, 1L);
      System.out.println("Employee loaded: " + employee);
  }

  public static void persistEmployee() {
      Employee employee = new Employee("Joe",
              "IT", Phone.cell("111-111-1111"));
      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', phones=[Phone{id=2, version=0, number='111-111-1111', type='cell'}]}
Update 1 starts, changing employee existing phone number
Employee updated 1: Employee{id=1, version=0, name='Joe', department='IT', phones=[Phone{id=2, version=1, number='111-222-3333', type='cell'}]}
Update 2 starts, changing employee existing phone number again
Employee updated 2: Employee{id=1, version=0, name='Joe', department='IT', phones=[Phone{id=2, version=2, number='222-333-3333', type='cell'}]}
Employee loaded: Employee{id=1, version=0, name='Joe', department='IT', phones=[Phone{id=2, version=2, number='222-333-3333', type='cell'}]}

As seen above, modifying existing phone numbers for an employee does not change the Employee's version number but it changes for the Phone entity which is being modified. That also means, in this scenario OptimisticLockException might be thrown on Phone's version conflict.

Adding new phones or deleting old phones, however, will affect the Employee's version instead of the existing Phone's versions:

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

  public static void main(String[] args) {
      try {
          persistEmployee();
          addNewPhone1();
          addNewPhone2();
          loadEmployee();
      } finally {
          entityManagerFactory.close();
      }
  }

  private static void addNewPhone1() {
      System.out.println("Update 1 starts, adding new phone number for the employee");
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, 1L);
      em.getTransaction().begin();
      employee.getPhones().add(Phone.work("222-222-2222"));
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee updated 1: " + employee);
  }

  private static void addNewPhone2() {
      System.out.println("Update 2 starts, adding another new phone number for the employee");
      EntityManager em = entityManagerFactory.createEntityManager();
      Employee employee = em.find(Employee.class, 1L);
      em.getTransaction().begin();
      employee.getPhones().add(Phone.home("333-333-3333"));
      em.getTransaction().commit();
      em.close();
      System.out.println("Employee updated 2: " + employee);
  }
    .............
}
Employee persisted: Employee{id=1, version=0, name='Joe', department='IT', phones=[Phone{id=2, version=0, number='111-111-1111', type='cell'}]}
Update 1 starts, adding new phone number for the employee
Employee updated 1: Employee{id=1, version=1, name='Joe', department='IT', phones=[Phone{id=2, version=0, number='111-111-1111', type='cell'}, Phone{id=3, version=0, number='222-222-2222', type='work'}]}
Update 2 starts, adding another new phone number for the employee
Employee updated 2: Employee{id=1, version=2, name='Joe', department='IT', phones=[Phone{id=2, version=0, number='111-111-1111', type='cell'}, Phone{id=3, version=0, number='222-222-2222', type='work'}, Phone{id=4, version=0, number='333-333-3333', type='home'}]}
Employee loaded: Employee{id=1, version=2, name='Joe', department='IT', phones=[Phone{id=2, version=0, number='111-111-1111', type='cell'}, Phone{id=3, version=0, number='222-222-2222', type='work'}, Phone{id=4, version=0, number='333-333-3333', type='home'}]}

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.
    Implements javax.persistence:javax.persistence-api version 2.1
  • JDK 1.8
  • Maven 3.3.9

Updating Relationship and Version Attribute Select All Download
  • jpa-optimistic-locking-and-relations
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • UpdatingRelationExample.java
          • resources
            • META-INF

    See Also