JPA乐观锁和悲观锁
JPA乐观锁和悲观锁
JPA中锁有6种:乐观锁有2种,悲观锁3种,另外1种无锁模式 NONE
- OPTIMISTIC 和READ锁模式相同,JPA2.0 任然支持READ模式,但是推荐使用OPTIMISTIC
- OPTIMISTIC_FORCE_INCREMENT 和WRITE模式相同,推荐使用
- PESSIMISTIC_READ 悲观锁,只要事务读实体,实体管理器就锁定当前实体,直到事务提交才释放锁。当多次重复读语句查询数据库时,可以使用。连续多次读,保证中间不会被修改。
- PESSIMISTIC_WRITE 只要事务更新实体,实体管理器就会对实体上锁。当多个并发更新事务出现更新失败几率大时,采用此模式。
- PESSIMISTIC_FORCE_INCREMENT 当事务读实体时,实体管理器对实体上锁,即使没有修改实体,版本号也会增加。
**JPA2.0 提供多种方法可以指定锁模式,可以尝试EntityManager的lock()和find(),EntityManager.refresh()方法可以恢复实体实例的状态。
Entity 类型定义
@Data
@Entity
public class Student {
@Id
@GeneratedValue
private long id;
private String name;
//添加1列用于版本号控制,乐观锁时使用
@Version
private long version;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + "'"+
'}';
}
}
悲观锁案例
@Before
public void add(){
EntityManager entityManager = JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
Student student=new Student();
student.setName("jake");
entityManager.persist(student);
entityManager.getTransaction().commit();
entityManager.close();
}
//悲观锁
@Test
public void write(){
EntityManager e1 = JpaUtil.getEntityManager();
EntityManager e2 = JpaUtil.getEntityManager();
e1.getTransaction().begin();
e2.getTransaction().begin();
//1、PESSIMISTIC_WRITE 悲观锁,写模式不提交事务另一个事务一直等待事务提交后查询
// Student student = e1.find(Student.class, 1L, LockModeType.PESSIMISTIC_WRITE);
// System.out.println("student = " + student);
// //这里会一直等待
// Student student1 = e2.find(Student.class, 1L, LockModeType.PESSIMISTIC_WRITE);
// System.out.println("student1 = " + student1);
//2、PESSIMISTIC_READ 悲观锁,读模式,其他事务也可以读取,而不会等待
Student student = e1.find(Student.class, 1L, LockModeType.PESSIMISTIC_READ);
System.out.println("student = " + student);
Student student1 = e2.find(Student.class, 1L, LockModeType.PESSIMISTIC_READ);
System.out.println("student1 = " + student1);
e2.getTransaction().commit();
e1.getTransaction().commit();
e2.close();
e1.close();
}
乐观锁案例
//乐观锁
@Test
public void optimistic(){
EntityManager e1 = JpaUtil.getEntityManager();
EntityManager e2 = JpaUtil.getEntityManager();
e1.getTransaction().begin();
e2.getTransaction().begin();
//乐观锁,修改数据时会更新表中的版本,如果版本和更新时版本不一致,则报异常
Student student = e1.find(Student.class, 1L, LockModeType.OPTIMISTIC);
student.setName("jake1");
Student student1 = e2.find(Student.class, 1L, LockModeType.OPTIMISTIC);
student1.setName("jake2");
e2.getTransaction().commit();
//e2 提交事务后,version+1;e1 之前查询的对象版本还是原来的,提交事务时判断本身版本和数据库版本不一致,报异常;
//javax.persistence.RollbackException: Error while committing the transaction
e1.getTransaction().commit();
e2.close();
e1.close();
}
控制台打印输出
update Student set name=?, version=? where id=? and version=?
Hibernate: select version from Student where id =?
Hibernate: update Student set name=?, version=? where id=? and version=?
//最后e1提交事务时,发现数据库版本号和当前实体版本号不一致,报异常
javax.persistence.RollbackException: Error while committing the transaction