您现在的位置是:首页 >其他 >Spring Data Jpa pessimistic locking网站首页其他

Spring Data Jpa pessimistic locking

你好y 2024-06-01 00:00:03
简介Spring Data Jpa pessimistic locking

我们可以保留两种类型的锁:独占锁和共享锁。当其他人持有共享锁时,我们可以读取但不能写入数据。为了修改或删除保留数据,我们需要有排它锁。

我们可以使用“SELECT … FOR UPDATE”语句获取独占锁。

锁定模式

JPA 规范定义了我们将要讨论的三种悲观锁模式:

  • PESSIMISTIC_READ允许我们获得共享锁并防止数据被更新或删除。
  • PESSIMISTIC_WRITE允许我们获得独占锁并防止数据被读取、更新或删除。
  • PESSIMISTIC_FORCE_INCREMENT 的工作方式与PESSIMISTIC_WRITE类似,它还会增加版本化实体的版本属性。
    它们都是LockModeType类的静态成员,允许事务获取数据库锁。它们都被保留,直到事务提交或回滚。

值得注意的是,我们一次只能获得一把锁。如果不可能,则抛出PersistenceException 。

PESSIMISTIC_READ

每当我们只想读取数据而不遇到脏读时,我们可以使用 PESSIMISTIC_READ (共享锁)。不过,我们将无法进行任何更新或删除。

有时我们使用的数据库不支持PESSIMISTIC_READ锁,所以我们可以获取PESSIMISTIC_WRITE 锁。

PESSIMISTIC_WRITE

任何需要获取数据锁并对其进行更改的事务都应该获取PESSIMISTIC_WRITE 锁。根据JPA规范,持有 PESSIMISTIC_WRITE锁将阻止其他事务读取、更新或删除数据。

请注意,一些数据库系统实现了 多版本并发控制,允许读者获取已经被阻塞的数据。

PESSIMISTIC_FORCE_INCREMENT

此锁的工作方式与PESSIMISTIC_WRITE类似,但引入它是为了与版本化实体协作——具有用@Version注释的属性的实体。

版本化实体的任何更新都可以在获得PESSIMISTIC_FORCE_INCREMENT锁之前进行。获取该锁会导致更新版本列。

由持久性提供者决定它是否支持未版本化实体的PESSIMISTIC_FORCE_INCREMENT 。如果没有,它会抛出 PersistenceException。

异常情况

最好知道在使用悲观锁定时可能会发生哪种异常。JPA规范提供了不同类型的异常:

PessimisticLockException表示获取锁或共享锁转排他锁失败,导致事务级回滚。
LockTimeoutException表示获取锁或将共享锁转换为独占锁超时并导致语句级回滚。
PersistenceException表示发生持久性问题。PersistenceException及其子类型,除了 NoResultException、NonUniqueResultException、LockTimeoutException 和 QueryTimeoutException,标记要回滚的活动事务。

锁定范围

锁定范围参数定义了如何处理被锁定实体的锁定关系。可以仅在查询中定义的单个实体上获得锁定,或者另外阻止其关系。

要配置范围,我们可以使用PessimisticLockScope枚举。它包含两个值:NORMALEXTENDED

我们可以通过将PessimisticLockScope值 作为 参数传递给EntityManager、Query、TypedQuery或NamedQuery的正确方法的参数’jakarta.persistence’来设置范围:

PessimisticLockScope.NORMAL

PessimisticLockScope.NORMAL是 默认范围。有了这个锁定范围,我们就锁定了实体本身。当与联合继承一起使用时,它还会锁定祖先。

让我们看一下包含两个实体的示例代码:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {

    @Id
    private Long id;
    private String name;
    private String lastName;

    // getters and setters
}

@Entity
public class Employee extends Person {

    private BigDecimal salary;

    // getters and setters
}

当我们想要获得对Employee 的锁定时,我们可以观察跨越这两个实体的SQL查询:

SELECT t0.ID, t0.DTYPE, t0.LASTNAME, t0.NAME, t1.ID, t1.SALARY 
FROM PERSON t0, EMPLOYEE t1 
WHERE ((t0.ID = ?) AND ((t1.ID = t0.ID) AND (t0.DTYPE = ?))) FOR UPDATE

PessimisticLockScope.EXTENDED

EXTENDED范围涵盖与NORMAL相同的功能。此外,它还能够阻止连接表中的相关实体。

简而言之,它适用于使用@ElementCollection或@OneToOne、@OneToMany等注释的实体以及@JoinTable。

让我们看一下带有@ElementCollection注解的示例代码:

@Entity
public class Customer {

    @Id
    private Long customerId;
    private String name;
    private String lastName;
    @ElementCollection
    @CollectionTable(name = "customer_address")
    private List<Address> addressList;

    // getters and setters
}

@Embeddable
public class Address {

    private String country;
    private String city;

    // getters and setters
}

让我们在搜索Customer实体时分析一些查询:

SELECT CUSTOMERID, LASTNAME, NAME 
FROM CUSTOMER WHERE (CUSTOMERID = ?) FOR UPDATE

SELECT CITY, COUNTRY, Customer_CUSTOMERID 
FROM customer_address 
WHERE (Customer_CUSTOMERID = ?) FOR UPDATE

我们可以看到有两个FOR UPDATE查询锁定了客户表中的一行以及连接表中的一行。

另一个需要注意的有趣事实是,并非所有持久性提供程序都支持锁定作用域。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。