From c0c319b13f5fef8c360b6090293e1c2e96e5bfb6 Mon Sep 17 00:00:00 2001 From: Patryk Kucharz Date: Mon, 30 Apr 2018 16:33:50 +0200 Subject: [PATCH 1/2] BAEL-1713: Pessimistic Locking in JPA Tests for the article. --- .../com/baeldung/hibernate/HibernateUtil.java | 38 ++--- .../hibernate/pessimisticlocking/Address.java | 34 ++++ .../pessimisticlocking/Customer.java | 58 +++++++ .../pessimisticlocking/Individual.java | 49 ++++++ .../PessimisticLockingCourse.java | 47 ++++++ .../PessimisticLockingEmployee.java | 27 ++++ .../PessimisticLockingStudent.java | 46 ++++++ ...asicPessimisticLockingIntegrationTest.java | 151 ++++++++++++++++++ .../PessimisticLockScopesIntegrationTest.java | 100 ++++++++++++ .../hibernate-pessimistic-locking.properties | 8 + .../pessimisticlocking/Address.java | 34 ++++ .../springdata/pessimisticlocking/Course.java | 50 ++++++ .../pessimisticlocking/Customer.java | 61 +++++++ .../pessimisticlocking/Employee.java | 27 ++++ .../pessimisticlocking/Individual.java | 49 ++++++ .../pessimisticlocking/Student.java | 48 ++++++ .../src/main/resources/application.properties | 2 +- .../PessimisticLockScopesIntegrationTest.java | 103 ++++++++++++ 18 files changed, 906 insertions(+), 26 deletions(-) create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Address.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Customer.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Individual.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingCourse.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingEmployee.java create mode 100644 hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingStudent.java create mode 100644 hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/BasicPessimisticLockingIntegrationTest.java create mode 100644 hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java create mode 100644 hibernate5/src/test/resources/hibernate-pessimistic-locking.properties create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Address.java create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Course.java create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Customer.java create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Employee.java create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Individual.java create mode 100644 persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Student.java create mode 100644 persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java index 130edec8cc..e8fdabebbc 100644 --- a/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java +++ b/hibernate5/src/main/java/com/baeldung/hibernate/HibernateUtil.java @@ -1,30 +1,12 @@ package com.baeldung.hibernate; -import com.baeldung.hibernate.pojo.Employee; -import com.baeldung.hibernate.pojo.EntityDescription; -import com.baeldung.hibernate.pojo.OrderEntry; -import com.baeldung.hibernate.pojo.OrderEntryIdClass; -import com.baeldung.hibernate.pojo.OrderEntryPK; -import com.baeldung.hibernate.pojo.PointEntity; -import com.baeldung.hibernate.pojo.PolygonEntity; -import com.baeldung.hibernate.pojo.Product; -import com.baeldung.hibernate.pojo.Phone; -import com.baeldung.hibernate.pojo.TemporalValues; -import com.baeldung.hibernate.pojo.Course; -import com.baeldung.hibernate.pojo.Student; -import com.baeldung.hibernate.pojo.User; -import com.baeldung.hibernate.pojo.UserProfile; -import com.baeldung.hibernate.pojo.inheritance.Animal; -import com.baeldung.hibernate.pojo.inheritance.Bag; -import com.baeldung.hibernate.pojo.inheritance.Book; -import com.baeldung.hibernate.pojo.inheritance.Car; -import com.baeldung.hibernate.pojo.inheritance.MyEmployee; -import com.baeldung.hibernate.pojo.inheritance.MyProduct; -import com.baeldung.hibernate.pojo.inheritance.Pen; -import com.baeldung.hibernate.pojo.inheritance.Person; -import com.baeldung.hibernate.pojo.inheritance.Pet; -import com.baeldung.hibernate.pojo.inheritance.Vehicle; - +import com.baeldung.hibernate.pessimisticlocking.Individual; +import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingCourse; +import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingEmployee; +import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingStudent; +import com.baeldung.hibernate.pojo.*; +import com.baeldung.hibernate.pojo.Person; +import com.baeldung.hibernate.pojo.inheritance.*; import org.apache.commons.lang3.StringUtils; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; @@ -82,6 +64,12 @@ public class HibernateUtil { metadataSources.addAnnotatedClass(PointEntity.class); metadataSources.addAnnotatedClass(PolygonEntity.class); metadataSources.addAnnotatedClass(com.baeldung.hibernate.pojo.Person.class); + metadataSources.addAnnotatedClass(Individual.class); + metadataSources.addAnnotatedClass(PessimisticLockingEmployee.class); + metadataSources.addAnnotatedClass(PessimisticLockingStudent.class); + metadataSources.addAnnotatedClass(PessimisticLockingCourse.class); + metadataSources.addAnnotatedClass(com.baeldung.hibernate.pessimisticlocking.Customer.class); + metadataSources.addAnnotatedClass(com.baeldung.hibernate.pessimisticlocking.Address.class); Metadata metadata = metadataSources.buildMetadata(); return metadata.getSessionFactoryBuilder() diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Address.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Address.java new file mode 100644 index 0000000000..c889cb6127 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Address.java @@ -0,0 +1,34 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.Embeddable; + +@Embeddable +public class Address { + + private String country; + private String city; + + public Address(String country, String city) { + this.country = country; + this.city = city; + } + + public Address() { + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Customer.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Customer.java new file mode 100644 index 0000000000..cb73cbc958 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Customer.java @@ -0,0 +1,58 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.*; +import java.util.List; + +@Entity +public class Customer { + + @Id + private Long customerId; + private String name; + private String lastName; + @ElementCollection + @CollectionTable(name = "customer_address") + private List
addressList; + + public Customer() { + } + + public Customer(Long customerId, String name, String lastName, List
addressList) { + this.customerId = customerId; + this.name = name; + this.lastName = lastName; + this.addressList = addressList; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public List
getAddressList() { + return addressList; + } + + public void setAddressList(List
addressList) { + this.addressList = addressList; + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Individual.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Individual.java new file mode 100644 index 0000000000..e491c09eb5 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/Individual.java @@ -0,0 +1,49 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +public class Individual { + + @Id + private Long id; + private String name; + private String lastName; + + public Individual(Long id, String name, String lastName) { + this.id = id; + this.name = name; + this.lastName = lastName; + } + + public Individual() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingCourse.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingCourse.java new file mode 100644 index 0000000000..aea7d5fc87 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingCourse.java @@ -0,0 +1,47 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.*; + +@Entity +public class PessimisticLockingCourse { + + @Id + private Long courseId; + private String name; + @ManyToOne + @JoinTable(name = "student_course") + private PessimisticLockingStudent student; + + public PessimisticLockingCourse(Long courseId, String name, PessimisticLockingStudent student) { + this.courseId = courseId; + this.name = name; + this.student = student; + } + + public PessimisticLockingCourse() { + } + + public Long getCourseId() { + return courseId; + } + + public void setCourseId(Long courseId) { + this.courseId = courseId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PessimisticLockingStudent getStudent() { + return student; + } + + public void setStudent(PessimisticLockingStudent students) { + this.student = students; + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingEmployee.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingEmployee.java new file mode 100644 index 0000000000..a1328cbdad --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingEmployee.java @@ -0,0 +1,27 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.Entity; +import java.math.BigDecimal; + +@Entity +public class PessimisticLockingEmployee extends Individual { + + private BigDecimal salary; + + public PessimisticLockingEmployee(Long id, String name, String lastName, BigDecimal salary) { + super(id, name, lastName); + this.salary = salary; + } + + public PessimisticLockingEmployee() { + super(); + } + + public BigDecimal getSalary() { + return salary; + } + + public void setSalary(BigDecimal average) { + this.salary = average; + } +} diff --git a/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingStudent.java b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingStudent.java new file mode 100644 index 0000000000..e6c5f476b4 --- /dev/null +++ b/hibernate5/src/main/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockingStudent.java @@ -0,0 +1,46 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import javax.persistence.*; +import java.util.List; + +@Entity +public class PessimisticLockingStudent { + + @Id + private Long id; + private String name; + @OneToMany(mappedBy = "student") + private List courses; + + public PessimisticLockingStudent(Long id, String name) { + this.id = id; + this.name = name; + } + + public PessimisticLockingStudent() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getCourses() { + return courses; + } + + public void setCourses(List courses) { + this.courses = courses; + } +} diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/BasicPessimisticLockingIntegrationTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/BasicPessimisticLockingIntegrationTest.java new file mode 100644 index 0000000000..f416c11d1f --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/BasicPessimisticLockingIntegrationTest.java @@ -0,0 +1,151 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import com.baeldung.hibernate.HibernateUtil; +import com.vividsolutions.jts.util.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.persistence.*; +import java.io.IOException; +import java.util.Arrays; + +public class BasicPessimisticLockingIntegrationTest { + + @BeforeClass + public static void setUp() throws IOException { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent student = new PessimisticLockingStudent(1L, "JOHN"); + PessimisticLockingCourse course = new PessimisticLockingCourse(1L, "MATH", student); + student.setCourses(Arrays.asList(course)); + entityManager.persist(course); + entityManager.persist(student); + entityManager.getTransaction() + .commit(); + entityManager.close(); + } + + @Test + public void givenFoundRecordWithPessimisticRead_whenFindingNewOne_PessimisticLockExceptionThrown() { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + entityManager.find(PessimisticLockingStudent.class, 1L, LockModeType.PESSIMISTIC_READ); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + entityManager2.find(PessimisticLockingStudent.class, 1L, LockModeType.PESSIMISTIC_READ); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + @Test + public void givenRecordWithPessimisticReadQuery_whenQueryingNewOne_PessimisticLockExceptionThrown() throws IOException { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + Query query = entityManager.createQuery("from Student where studentId = :studentId"); + query.setParameter("studentId", 1L); + query.setLockMode(LockModeType.PESSIMISTIC_WRITE); + query.getResultList(); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + Query query2 = entityManager2.createQuery("from Student where studentId = :studentId"); + query2.setParameter("studentId", 1L); + query2.setLockMode(LockModeType.PESSIMISTIC_READ); + query2.getResultList(); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + @Test + public void givenRecordWithPessimisticReadLock_whenFindingNewOne_PessimisticLockExceptionThrown() { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent = entityManager.find(PessimisticLockingStudent.class, 1L); + entityManager.lock(resultStudent, LockModeType.PESSIMISTIC_READ); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + entityManager2.find(PessimisticLockingStudent.class, 1L, LockModeType.PESSIMISTIC_FORCE_INCREMENT); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + @Test + public void givenRecordAndRefreshWithPessimisticRead_whenFindingWithPessimisticWrite_PessimisticLockExceptionThrown() { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent = entityManager.find(PessimisticLockingStudent.class, 1L); + entityManager.refresh(resultStudent, LockModeType.PESSIMISTIC_FORCE_INCREMENT); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + entityManager2.find(PessimisticLockingStudent.class, 1L, LockModeType.PESSIMISTIC_WRITE); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + @Test + public void givenRecordWithPessimisticRead_whenUpdatingRecord_PessimisticLockExceptionThrown() { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent = entityManager.find(PessimisticLockingStudent.class, 1L); + entityManager.refresh(resultStudent, LockModeType.PESSIMISTIC_READ); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent2 = entityManager2.find(PessimisticLockingStudent.class, 1L); + resultStudent2.setName("Change"); + entityManager2.persist(resultStudent2); + entityManager2.getTransaction() + .commit(); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + @Test + public void givenRecordWithPessimisticWrite_whenUpdatingRecord_PessimisticLockExceptionThrown() { + try { + EntityManager entityManager = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent = entityManager.find(PessimisticLockingStudent.class, 1L); + entityManager.refresh(resultStudent, LockModeType.PESSIMISTIC_WRITE); + + EntityManager entityManager2 = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent resultStudent2 = entityManager2.find(PessimisticLockingStudent.class, 1L); + resultStudent2.setName("Change"); + entityManager2.persist(resultStudent2); + entityManager2.getTransaction() + .commit(); + + entityManager.close(); + entityManager2.close(); + } catch (Exception e) { + Assert.isTrue(e instanceof PessimisticLockException); + } + } + + protected static EntityManager getEntityManagerWithOpenTransaction() throws IOException { + String propertyFileName = "hibernate-pessimistic-locking.properties"; + EntityManager entityManager = HibernateUtil.getSessionFactory(propertyFileName) + .openSession(); + entityManager.getTransaction() + .begin(); + + return entityManager; + } + +} diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java new file mode 100644 index 0000000000..c774c6eaa4 --- /dev/null +++ b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java @@ -0,0 +1,100 @@ +package com.baeldung.hibernate.pessimisticlocking; + +import com.baeldung.hibernate.HibernateUtil; +import org.junit.Test; + +import javax.persistence.EntityManager; +import javax.persistence.LockModeType; +import javax.persistence.PessimisticLockScope; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class PessimisticLockScopesIntegrationTest { + + @Test + public void givenEclipseEntityWithJoinInheritance_whenNormalLock_thenShouldChildAndParentEntity() throws IOException { + EntityManager em = getEntityManagerWithOpenTransaction(); + PessimisticLockingEmployee employee = new PessimisticLockingEmployee(1L, "A", "B", new BigDecimal(4.5)); + em.persist(employee); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + PessimisticLockingEmployee foundEmployee = em.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundEmployee = em.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + @Test + public void givenEclipseEntityWithElementCollection_whenNormalLock_thenShouldLockOnlyOwningEntity() throws IOException { + EntityManager em = getEntityManagerWithOpenTransaction(); + Address address = new Address("Poland", "Warsaw"); + Customer customer = new Customer(1L, "A", "B", Arrays.asList(address)); + em.persist(customer); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + Customer foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + @Test + public void givenEclipseEntityWithOneToMany_whenNormalLock_thenShouldLockOnlyOwningEntity() throws IOException { + EntityManager em = getEntityManagerWithOpenTransaction(); + PessimisticLockingStudent student = new PessimisticLockingStudent(1L, "JOE"); + PessimisticLockingCourse course = new PessimisticLockingCourse(1L, "COURSE", student); + student.setCourses(Arrays.asList(course)); + em.persist(course); + em.persist(student); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + PessimisticLockingCourse foundCourse = em.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundCourse = em.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + protected EntityManager getEntityManagerWithOpenTransaction() throws IOException { + String propertyFileName = "hibernate-pessimistic-locking.properties"; + EntityManager entityManager = HibernateUtil.getSessionFactory(propertyFileName) + .openSession(); + entityManager.getTransaction() + .begin(); + + return entityManager; + } + +} diff --git a/hibernate5/src/test/resources/hibernate-pessimistic-locking.properties b/hibernate5/src/test/resources/hibernate-pessimistic-locking.properties new file mode 100644 index 0000000000..c76bd3358b --- /dev/null +++ b/hibernate5/src/test/resources/hibernate-pessimistic-locking.properties @@ -0,0 +1,8 @@ +hibernate.connection.driver_class=org.h2.Driver +hibernate.connection.url=jdbc:h2:mem:mydb1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=100;MVCC=FALSE +hibernate.connection.username=sa +hibernate.connection.autocommit=true +hibernate.dialect=org.hibernate.dialect.H2Dialect + +hibernate.show_sql=true +hibernate.hbm2ddl.auto=create-drop \ No newline at end of file diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Address.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Address.java new file mode 100644 index 0000000000..b62889208c --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Address.java @@ -0,0 +1,34 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.Embeddable; + +@Embeddable +public class Address { + + private String country; + private String city; + + public Address(String country, String city) { + this.country = country; + this.city = city; + } + + public Address() { + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Course.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Course.java new file mode 100644 index 0000000000..8d90659f3e --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Course.java @@ -0,0 +1,50 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; + +@Entity +public class Course { + + @Id + private Long courseId; + private String name; + @ManyToOne + @JoinTable(name = "student_course") + private Student student; + + public Course(Long courseId, String name, Student student) { + this.courseId = courseId; + this.name = name; + this.student = student; + } + + public Course() { + } + + public Long getCourseId() { + return courseId; + } + + public void setCourseId(Long courseId) { + this.courseId = courseId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Student getStudent() { + return student; + } + + public void setStudent(Student students) { + this.student = students; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Customer.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Customer.java new file mode 100644 index 0000000000..f06a676de8 --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Customer.java @@ -0,0 +1,61 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.CollectionTable; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.Id; +import java.util.List; + +@Entity +public class Customer { + + @Id + private Long customerId; + private String name; + private String lastName; + @ElementCollection + @CollectionTable(name = "customer_address") + private List
addressList; + + public Customer() { + } + + public Customer(Long customerId, String name, String lastName, List
addressList) { + this.customerId = customerId; + this.name = name; + this.lastName = lastName; + this.addressList = addressList; + } + + public Long getCustomerId() { + return customerId; + } + + public void setCustomerId(Long customerId) { + this.customerId = customerId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public List
getAddressList() { + return addressList; + } + + public void setAddressList(List
addressList) { + this.addressList = addressList; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Employee.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Employee.java new file mode 100644 index 0000000000..d09b123225 --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Employee.java @@ -0,0 +1,27 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.Entity; +import java.math.BigDecimal; + +@Entity +public class Employee extends Individual { + + private BigDecimal salary; + + public Employee(Long id, String name, String lastName, BigDecimal salary) { + super(id, name, lastName); + this.salary = salary; + } + + public Employee() { + super(); + } + + public BigDecimal getSalary() { + return salary; + } + + public void setSalary(BigDecimal average) { + this.salary = average; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Individual.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Individual.java new file mode 100644 index 0000000000..7edaaace54 --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Individual.java @@ -0,0 +1,49 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; + +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +public class Individual { + + @Id + private Long id; + private String name; + private String lastName; + + public Individual(Long id, String name, String lastName) { + this.id = id; + this.name = name; + this.lastName = lastName; + } + + public Individual() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Student.java b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Student.java new file mode 100644 index 0000000000..f613aab0f6 --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/main/java/com/baeldung/eclipselink/springdata/pessimisticlocking/Student.java @@ -0,0 +1,48 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import java.util.List; + +@Entity +public class Student { + + @Id + private Long id; + private String name; + @OneToMany(mappedBy = "student") + private List courses; + + public Student(Long id, String name) { + this.id = id; + this.name = name; + } + + public Student() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getCourses() { + return courses; + } + + public void setCourses(List courses) { + this.courses = courses; + } +} diff --git a/persistence-modules/spring-data-eclipselink/src/main/resources/application.properties b/persistence-modules/spring-data-eclipselink/src/main/resources/application.properties index 549c0b4ada..5874482e7a 100644 --- a/persistence-modules/spring-data-eclipselink/src/main/resources/application.properties +++ b/persistence-modules/spring-data-eclipselink/src/main/resources/application.properties @@ -1,2 +1,2 @@ -spring.datasource.url=jdbc:h2:mem:test +spring.datasource.url=jdbc:h2:mem:test;MVCC=FALSE;LOCK_TIMEOUT=100; spring.jpa.show-sql=true \ No newline at end of file diff --git a/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java b/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java new file mode 100644 index 0000000000..e84e991d5d --- /dev/null +++ b/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java @@ -0,0 +1,103 @@ +package com.baeldung.eclipselink.springdata.pessimisticlocking; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class PessimisticLockScopesIntegrationTest { + + @Autowired + EntityManagerFactory entityManagerFactory; + + @Test + public void givenEclipseEntityWithJoinInheritance_whenNormalLock_thenShouldChildAndParentEntity() { + EntityManager em = getEntityManagerWithOpenTransaction(); + Employee employee = new Employee(1L, "A", "B", new BigDecimal(4.5)); + em.persist(employee); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + Employee foundEmployee = em.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundEmployee = em.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + @Test + public void givenEclipseEntityWithElementCollection_whenNormalLock_thenShouldLockOnlyOwningEntity() { + EntityManager em = getEntityManagerWithOpenTransaction(); + Address address = new Address("Poland", "Warsaw"); + Customer customer = new Customer(1L, "A", "B", Arrays.asList(address)); + em.persist(customer); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + Customer foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + @Test + public void givenEclipseEntityWithOneToMany_whenNormalLock_thenShouldLockOnlyOwningEntity() { + EntityManager em = getEntityManagerWithOpenTransaction(); + Student student = new Student(1L, "JOE"); + Course course = new Course(1L, "COURSE", student); + student.setCourses(Arrays.asList(course)); + em.persist(course); + em.persist(student); + em.getTransaction() + .commit(); + em.close(); + + // NORMAL SCOPE + em = getEntityManagerWithOpenTransaction(); + Course foundCourse = em.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em.getTransaction() + .rollback(); + + // EXTENDED SCOPE + Map map = new HashMap<>(); + map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); + + em = getEntityManagerWithOpenTransaction(); + foundCourse = em.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + } + + protected EntityManager getEntityManagerWithOpenTransaction() { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + entityManager.getTransaction() + .begin(); + return entityManager; + } +} From fd11e089fe49e5d52c18ef3c5575943fa5f9f9e4 Mon Sep 17 00:00:00 2001 From: Patryk Kucharz Date: Thu, 10 May 2018 20:53:58 +0200 Subject: [PATCH 2/2] BAEL-1713: Pessimistic Locking in JPA Improvments for tests. --- .../PessimisticLockScopesIntegrationTest.java | 53 +++++++++++------- .../PessimisticLockScopesIntegrationTest.java | 55 ++++++++++++------- 2 files changed, 69 insertions(+), 39 deletions(-) diff --git a/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java index c774c6eaa4..ac56ab7133 100644 --- a/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java +++ b/hibernate5/src/test/java/com/baeldung/hibernate/pessimisticlocking/PessimisticLockScopesIntegrationTest.java @@ -17,52 +17,62 @@ public class PessimisticLockScopesIntegrationTest { @Test public void givenEclipseEntityWithJoinInheritance_whenNormalLock_thenShouldChildAndParentEntity() throws IOException { EntityManager em = getEntityManagerWithOpenTransaction(); - PessimisticLockingEmployee employee = new PessimisticLockingEmployee(1L, "A", "B", new BigDecimal(4.5)); + PessimisticLockingEmployee employee = new PessimisticLockingEmployee(1L, "JOHN", "SMITH", new BigDecimal(4.5)); em.persist(employee); em.getTransaction() .commit(); em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - PessimisticLockingEmployee foundEmployee = em.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + PessimisticLockingEmployee foundEmployee = em2.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundEmployee = em.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundEmployee = em3.find(PessimisticLockingEmployee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em3.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } @Test - public void givenEclipseEntityWithElementCollection_whenNormalLock_thenShouldLockOnlyOwningEntity() throws IOException { + public void givenEntityWithElementCollection_whenLock_thenHibernateExtendedScopeLockOnlyOwningEntity() throws IOException { EntityManager em = getEntityManagerWithOpenTransaction(); Address address = new Address("Poland", "Warsaw"); - Customer customer = new Customer(1L, "A", "B", Arrays.asList(address)); + Customer customer = new Customer(1L, "JOE", "DOE", Arrays.asList(address)); em.persist(customer); em.getTransaction() .commit(); em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - Customer foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + Customer foundCustomer = em2.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundCustomer = em3.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em2.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } @Test - public void givenEclipseEntityWithOneToMany_whenNormalLock_thenShouldLockOnlyOwningEntity() throws IOException { + public void givenEntityWithOneToMany_whenLock_thenHibernateExtendedScopeLockOnlyOwningEntity() throws IOException { EntityManager em = getEntityManagerWithOpenTransaction(); PessimisticLockingStudent student = new PessimisticLockingStudent(1L, "JOE"); PessimisticLockingCourse course = new PessimisticLockingCourse(1L, "COURSE", student); @@ -74,17 +84,22 @@ public class PessimisticLockScopesIntegrationTest { em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - PessimisticLockingCourse foundCourse = em.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + PessimisticLockingCourse foundCourse = em2.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundCourse = em.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundCourse = em3.find(PessimisticLockingCourse.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em3.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } protected EntityManager getEntityManagerWithOpenTransaction() throws IOException { diff --git a/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java b/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java index e84e991d5d..6ee40fac9a 100644 --- a/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java +++ b/persistence-modules/spring-data-eclipselink/src/test/java/com/baeldung/eclipselink/springdata/pessimisticlocking/PessimisticLockScopesIntegrationTest.java @@ -22,54 +22,64 @@ public class PessimisticLockScopesIntegrationTest { EntityManagerFactory entityManagerFactory; @Test - public void givenEclipseEntityWithJoinInheritance_whenNormalLock_thenShouldChildAndParentEntity() { + public void givenEntityWithJoinInheritance_whenLock_thenNormalAndExtendScopesLockParentAndChildEntity() { EntityManager em = getEntityManagerWithOpenTransaction(); - Employee employee = new Employee(1L, "A", "B", new BigDecimal(4.5)); + Employee employee = new Employee(1L, "JOE", "DOE", new BigDecimal(4.5)); em.persist(employee); em.getTransaction() .commit(); em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - Employee foundEmployee = em.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + Employee foundEmployee = em2.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundEmployee = em.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundEmployee = em3.find(Employee.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em3.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } @Test - public void givenEclipseEntityWithElementCollection_whenNormalLock_thenShouldLockOnlyOwningEntity() { + public void givenEntityWithElementCollection_whenLock_thenExtendScopeLocksAlsoCollectionTable() { EntityManager em = getEntityManagerWithOpenTransaction(); Address address = new Address("Poland", "Warsaw"); - Customer customer = new Customer(1L, "A", "B", Arrays.asList(address)); + Customer customer = new Customer(1L, "JOHN", "SMITH", Arrays.asList(address)); em.persist(customer); em.getTransaction() .commit(); em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - Customer foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + Customer foundCustomer = em2.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundCustomer = em.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundCustomer = em3.find(Customer.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em3.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } @Test - public void givenEclipseEntityWithOneToMany_whenNormalLock_thenShouldLockOnlyOwningEntity() { + public void givenEclipseEntityWithOneToMany_whenLock_thenExtendedLockAlsoJoinTable() { EntityManager em = getEntityManagerWithOpenTransaction(); Student student = new Student(1L, "JOE"); Course course = new Course(1L, "COURSE", student); @@ -81,17 +91,22 @@ public class PessimisticLockScopesIntegrationTest { em.close(); // NORMAL SCOPE - em = getEntityManagerWithOpenTransaction(); - Course foundCourse = em.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE); - em.getTransaction() + EntityManager em2 = getEntityManagerWithOpenTransaction(); + Course foundCourse = em2.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE); + em2.getTransaction() .rollback(); // EXTENDED SCOPE Map map = new HashMap<>(); map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED); - em = getEntityManagerWithOpenTransaction(); - foundCourse = em.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + EntityManager em3 = getEntityManagerWithOpenTransaction(); + foundCourse = em3.find(Course.class, 1L, LockModeType.PESSIMISTIC_WRITE, map); + em3.getTransaction() + .rollback(); + + em2.close(); + em3.close(); } protected EntityManager getEntityManagerWithOpenTransaction() {