BAEL-1713: Pessimistic Locking in JPA
Tests for the article.
This commit is contained in:
parent
7140229ca0
commit
c0c319b13f
|
@ -1,30 +1,12 @@
|
||||||
package com.baeldung.hibernate;
|
package com.baeldung.hibernate;
|
||||||
|
|
||||||
import com.baeldung.hibernate.pojo.Employee;
|
import com.baeldung.hibernate.pessimisticlocking.Individual;
|
||||||
import com.baeldung.hibernate.pojo.EntityDescription;
|
import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingCourse;
|
||||||
import com.baeldung.hibernate.pojo.OrderEntry;
|
import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingEmployee;
|
||||||
import com.baeldung.hibernate.pojo.OrderEntryIdClass;
|
import com.baeldung.hibernate.pessimisticlocking.PessimisticLockingStudent;
|
||||||
import com.baeldung.hibernate.pojo.OrderEntryPK;
|
import com.baeldung.hibernate.pojo.*;
|
||||||
import com.baeldung.hibernate.pojo.PointEntity;
|
import com.baeldung.hibernate.pojo.Person;
|
||||||
import com.baeldung.hibernate.pojo.PolygonEntity;
|
import com.baeldung.hibernate.pojo.inheritance.*;
|
||||||
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 org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.boot.Metadata;
|
import org.hibernate.boot.Metadata;
|
||||||
|
@ -82,6 +64,12 @@ public class HibernateUtil {
|
||||||
metadataSources.addAnnotatedClass(PointEntity.class);
|
metadataSources.addAnnotatedClass(PointEntity.class);
|
||||||
metadataSources.addAnnotatedClass(PolygonEntity.class);
|
metadataSources.addAnnotatedClass(PolygonEntity.class);
|
||||||
metadataSources.addAnnotatedClass(com.baeldung.hibernate.pojo.Person.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();
|
Metadata metadata = metadataSources.buildMetadata();
|
||||||
return metadata.getSessionFactoryBuilder()
|
return metadata.getSessionFactoryBuilder()
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Address> addressList;
|
||||||
|
|
||||||
|
public Customer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer(Long customerId, String name, String lastName, List<Address> 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<Address> getAddressList() {
|
||||||
|
return addressList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddressList(List<Address> addressList) {
|
||||||
|
this.addressList = addressList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<PessimisticLockingCourse> 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<PessimisticLockingCourse> getCourses() {
|
||||||
|
return courses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCourses(List<PessimisticLockingCourse> courses) {
|
||||||
|
this.courses = courses;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, Object> 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<String, Object> 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<String, Object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Address> addressList;
|
||||||
|
|
||||||
|
public Customer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Customer(Long customerId, String name, String lastName, List<Address> 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<Address> getAddressList() {
|
||||||
|
return addressList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddressList(List<Address> addressList) {
|
||||||
|
this.addressList = addressList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Course> 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<Course> getCourses() {
|
||||||
|
return courses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCourses(List<Course> courses) {
|
||||||
|
this.courses = courses;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
spring.jpa.show-sql=true
|
|
@ -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<String, Object> 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<String, Object> 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<String, Object> 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue