Spring Data JPA进阶:事务和锁
参考
https://wangchengming.blog.csdn.net/article/details/90898302
锁
锁的使用
1、在持久层的方法上添加 @Lock
注解并选择锁的类型即可
interface UserRepository extends Repository<User, Long> {
@Lock(LockModeType.READ)
List<User> findByLastname(String lastname);
}
需要注意的是,要在事务里面使用锁
,否则会报 TransactionRequiredException
异常。
2、在 EntityManager
中使用锁
// find的时候指定锁
entityManager.find(Department.class, 1, LockModeType.PESSIMISTIC_READ);
// find后锁住一个实体
Department department = entityManager.find(Department.class, 1);
entityManager.lock(department, LockModeType.PESSIMISTIC_READ);
// 锁住query results
TypedQuery<Department> query = entityManager
.createQuery("select d from Department d", Department.class);
query.setLockMode(LockModeType.PESSIMISTIC_READ);
List<Department> departments = query.getResultList();
锁的类型
首先 READ
与 WRITE
是别名,NONE
是无锁,所以不作过多解释。主要介绍这五个锁:
- OPTIMISTIC:读操作时不会更新
Version
字段的值,只有在写操作的时候会 - OPTIMISTIC_FORCE_INCREMENT:在读和写操作时都会更新
Version
字段的值 - PESSIMISTIC_READ
- PESSIMISTIC_WRITE
- PESSIMISTIC_FORCE_INCREMENT
乐观锁和悲观锁
这两种锁都可以避免两个事务中的其中一个,在不知情的情况下覆盖另一个事务的数据。以 OPTIMISTIC
开头的代表 “乐观锁”,以 PESSIMISTIC
开头的是 “悲观锁”。
乐观锁
乐观锁是在 JPA 层面实现的
。顾名思义,乐观锁比较 “乐观”,它假设冲突很少发生,即使发生,抛出一个错误也比想办法避免它们更容易接受和简单。核心思想就是在实体中定义一个 version
字段,可以是数值类型或时间戳类型。在读取的时候,就取到这个字段。然后在修改了实体的数值,保存的时候,就会去数据库对比这个 version
,如果 version
一致,就执行更新,否则就不执行更新。
@Entity
public class Student{
@Id
private int id;
private String name;
private BigDecimal money;
@Version
private int version;
// getters and setters
}
对于实体中有添加了 @Version
注解的列,JPA 会自动对该实体使用乐观锁 OPTIMISTIC_FORCE_INCREMENT
。你不需要使用任何锁命令。但是你也可以在 Repository
里面通过 @Lock
注解去自定义自己想要的锁。
使用乐观锁,打印 sql,会发现在执行 save
方法的时候,会把 version
作为条件。
也可以不使用 Spring Data JPA 的乐观锁,而自己新增一个字段,在执行更新操作的时候手动去实现。
悲观锁
悲观锁是在数据库层面实现的
,JPA 只是把请求交给数据库。悲观锁比较 “悲观”,意思是只要开启事务的时候,就会对数据进行加锁操作,在事务结束后才解锁。
而有些数据库是不支持 PESSIMISTIC_READ
的,JPA 规范中说到,不需要提供 PESSIMISTIC_READ
(因为许多 DB 只支持 WRITE
锁):
允许 JPA 实现用 LockModeType.PESSIMISTIC_WRITE
来代替 LockModeType.PESSIMISTIC_READ
,但是反之不可。
悲观写锁 PESSIMISTIC_WRITE
是基于 SELECT … FOR UPDATE
这种 SQL 语法来实现的:
select
id
from
product
where
id =?
and version =? for update
悲观读锁 PESSIMISTIC_READ
很多数据库不支持。在 MYSQL 中,会生成 SELECT …. LOCK INSHARE MODE
。
PESSIMISTIC_FORCE_INCREMENT
会使用悲观写锁,但不管有没有修改数据,都会更新 Version
字段的值。
只能在查询语句上使用锁
Spring Data JPA 目前只支持在 查询语句
(SELECT语句)中使用 @Lock
注解,否则会抛异常。
全部评论