Spring Boot2+JPA之悲观锁和乐观锁实战教程

下面我就为您讲解Spring Boot2 + JPA悲观锁和乐观锁实战教程的完整攻略。

1. 悲观锁实战

1.1 悲观锁的概念

悲观锁是指,当在执行某一操作时,认为别的并发操作会对其产生影响,因此在执行前进行加锁,使得其他并发操作无法操作,直到该操作完成释放锁。

1.2 悲观锁的实现

在JPA中,实现悲观锁可以通过 @Lock 注解来实现。具体实现方法如下:

/**
 * 根据id查询订单
 * @param id 订单id
 * @return 订单信息
 */
@Transactional(readOnly = true)
@Lock(LockModeType.PESSIMISTIC_WRITE)    // 悲观写锁
OrderEntity findById(Integer id);

在查询订单时,使用了 @Lock(LockModeType.PESSIMISTIC_WRITE) 注解,并传入参数 LockModeType.PESSIMISTIC_WRITE,表示使用悲观写锁。

1.3 悲观锁实例1

下面我们以一个简单的订单支付接口(为了订单操作的幂等性,只进行部分支付)为例来实现悲观锁。

首先,在 OrderEntity 实体类中增加一个 version 字段,用于实现乐观锁。

@Entity
@Table(name = "t_order")
public class OrderEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    ...

    @Version
    private Integer version;    // 版本号

    ...
}

然后,在订单支付接口实现中,使用 findByIdAndVersion 方法查询订单时,加上 @Lock(LockModeType.PESSIMISTIC_WRITE) 注解,表示使用悲观写锁。

@Service
public class OrderServiceImpl implements OrderService {

    ...

    @Transactional(rollbackFor = Exception.class)
    public void pay(Integer id, BigDecimal amount) {
        OrderEntity order = orderRepository.findByIdAndVersion(id, 0, LockModeType.PESSIMISTIC_WRITE)
                .orElseThrow(() -> new BusinessException(ErrorCode.ORDER_NOT_FOUND));

        order.setAmountPaid(order.getAmountPaid().add(amount));
        orderRepository.save(order);
    }

    ...
}

在上述代码中,我们使用 findByIdAndVersion 方法查询订单时,加上 @Lock(LockModeType.PESSIMISTIC_WRITE) 注解,并传入参数 LockModeType.PESSIMISTIC_WRITE,表示使用悲观写锁。这样保证在进行部分支付时,其他线程无法对该订单进行支付操作。

1.4 悲观锁实例2

接下来,我们再以一个更复杂的需求为例,来实现悲观锁。假设我们有一张商品表,每当有用户购买该商品时,库存数量会减1。但是,为了避免超卖现象,我们需要在减库存前查询库存数量并使用悲观写锁进行加锁,以确保操作的正确性。

首先,在 ProductEntity 实体类中增加一个 version 字段,用于实现乐观锁。

@Entity
@Table(name = "t_product")
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    ...

    @Version
    private Integer version;    // 版本号

    ...
}

然后,在商品服务实现中,使用 findById 方法查询商品时,加上 @Lock(LockModeType.PESSIMISTIC_WRITE) 注解,表示使用悲观写锁。

@Service
public class ProductServiceImpl implements ProductService {

    ...

    @Transactional(rollbackFor = Exception.class)
    public void purchase(Integer productId) {
        ProductEntity product = productRepository.findById(productId)
                .orElseThrow(() -> new BusinessException(ErrorCode.PRODUCT_NOT_FOUND));

        synchronized (ProductServiceImpl.class) {
            if (product.getStock() > 0) {
                product.setStock(product.getStock() - 1);
                productRepository.save(product);
            } else {
                throw new BusinessException(ErrorCode.STOCK_NOT_ENOUGH);
            }
        }
    }

    ...
}

在上述代码中,我们使用 findById 方法查询商品时,加上 @Lock(LockModeType.PESSIMISTIC_WRITE) 注解,并传入参数 LockModeType.PESSIMISTIC_WRITE,表示使用悲观写锁。然后在操作库存前使用 synchronized 关键字加锁,以确保操作的正确性。

2. 乐观锁实战

2.1 乐观锁的概念

乐观锁是指,当在执行某一操作时,认为别的并发操作不会对其产生影响,因此在执行前不进行加锁,而是在更新操作时判断该记录的版本是否已被其他并发操作修改,如果该记录的版本未被修改,则更新成功;否则更新失败,并重新尝试更新。

2.2 乐观锁的实现

在JPA中,实现乐观锁可以通过 @Version 注解来实现。当更新实体时,如果版本号发生变化,则更新失败并抛出 OptimisticLockException 异常。

2.3 乐观锁实例1

我们仍然以订单为例,使用 @Version 注解实现乐观锁。

首先,在 OrderEntity 实体类中增加一个 version 字段,用于实现乐观锁。

@Entity
@Table(name = "t_order")
public class OrderEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    ...

    @Version
    private Integer version;    // 版本号

    ...
}

然后,在订单支付接口实现中,使用 findById 方法查询订单时,加上版本号判断,并在更新订单时增加版本号。

@Service
public class OrderServiceImpl implements OrderService {

    ...

    @Transactional(rollbackFor = Exception.class)
    public void pay(Integer id, BigDecimal amount) {
        OrderEntity order = orderRepository.findById(id)
                .orElseThrow(() -> new BusinessException(ErrorCode.ORDER_NOT_FOUND));

        if (order.getVersion() == 0) {
            order.setVersion(1);
        }

        order.setAmountPaid(order.getAmountPaid().add(amount));
        order.setVersion(order.getVersion() + 1);
        orderRepository.save(order);
    }

    ...
}

在上述代码中,我们在查询订单时,判断订单的版本号是否为0,如果是,则设置版本号为1;否则在更新订单时,将版本号加1。

2.4 乐观锁实例2

再以商品为例,使用 @Version 注解实现乐观锁。

首先,在 ProductEntity 实体类中增加一个 version 字段,用于实现乐观锁。

@Entity
@Table(name = "t_product")
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    ...

    @Version
    private Integer version;    // 版本号

    ...
}

然后,在商品服务实现中,使用 findById 方法查询商品时,在更新商品时增加版本号,并在更新时判断版本号是否正确。

@Service
public class ProductServiceImpl implements ProductService {

    ...

    @Transactional(rollbackFor = Exception.class)
    public void purchase(Integer productId) {
        ProductEntity product = productRepository.findById(productId)
                .orElseThrow(() -> new BusinessException(ErrorCode.PRODUCT_NOT_FOUND));

        if (product.getStock() > 0) {
            product.setStock(product.getStock() - 1);
            product.setVersion(product.getVersion() + 1);
            productRepository.save(product);
        } else {
            throw new BusinessException(ErrorCode.STOCK_NOT_ENOUGH);
        }
    }

    ...
}

在上述代码中,我们在更新商品时,将版本号加1,并在更新时判断版本号是否正确,如果版本号不正确,则更新失败并抛出 OptimisticLockException 异常。

这就是Spring Boot2+JPA悲观锁和乐观锁实战教程的完整攻略,希望对您有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot2+JPA之悲观锁和乐观锁实战教程 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • python、java等哪一门编程语言适合人工智能?

    针对“哪一门编程语言适合人工智能”这个问题,答案并不是非常确定。不同的编程语言和不同的开发环境都有其适用的领域。 Python是人工智能领域的主要语言之一。它拥有成熟且强大的第三方库,例如NumPy、Pandas、Matplotlib和scikit-learn等,在数据处理和分析方面非常出色。同时,Python也有很多用于深度学习的框架,例如TensorFl…

    Java 2023年5月19日
    00
  • java页面中文乱码的解决办法

    针对你提出的问题:“java页面中文乱码的解决办法”,我准备分享以下完整攻略: 1. 确认编码方式 首先要确认在哪些地方需要进行编码方式的确认和设置,这些地方包括: 页面的 meta 标签 操作系统的全局编码设置 服务器的编码设置 web.xml 我们需要依次去检查这些地方是否将编码方式设置为正确的 UTF-8。 下面给出两个示例。 示例 1:在 meta …

    Java 2023年5月20日
    00
  • Java泛型与数据库应用实例详解

    Java泛型与数据库应用实例详解 什么是Java泛型? Java泛型是Java SE 5中引入的一项语言特性,它提供了一种编写泛化代码的方法,能够提高代码的通用性和复用性,从而提高了代码的可维护性和可扩展性。 Java泛型的语法 Java泛型使用尖括号<>来规定类型参数,语法格式如下: public class GenericClass<T…

    Java 2023年5月20日
    00
  • Java 数据结构与算法系列精讲之字符串暴力匹配

    Java 数据结构与算法系列精讲之字符串暴力匹配 1. 基本概念 字符串匹配是一种非常常见的算法问题。给定一个字符串 A 和一个模式串 B,要求在字符串 A 中查找是否有 B 出现的位置,如果有,则返回第一次出现的位置,否则返回-1。字符串暴力匹配就是一种解决此问题的算法,它的基本思路就是从字符串 A 中从头开始一个字符一个字符地去匹配模式串 B 的每个字符…

    Java 2023年5月19日
    00
  • MAVEN的安装配置与IDEA整合超详细教程

    下面我来详细讲解“MAVEN的安装配置与IDEA整合超详细教程”。 安装MAVEN 1. 下载MAVEN 首先,我们需要从官方网站下载MAVEN。目前最新版本是3.8.1,可以在Maven官网找到对应的下载链接。选择合适自己的版本并下载。 2. 安装MAVEN 下载完成之后,我们需要将MAVEN解压到某个目录下(比如D盘的maven目录下),然后将MAVEN…

    Java 2023年5月20日
    00
  • Java多维数组详解

    Java多维数组详解   什么是多维数组 Java 数组可以是一维或多维。一维数组就是最普通数组,是一列数据的线性序列,而多维数组相对而言就更加复杂。 多维数组就是数组中包含数组,类似于矩阵。 一个一维数组表示一行 多个一维数组表示多行   声明和初始化多维数组 多维数组的声明和初始化与一维数组非常类似,二维数组的初始化需要给出行数和列数…

    Java 2023年5月26日
    00
  • Java与MySQL时间不一致问题解决

    下面是Java与MySQL时间不一致问题的解决攻略。 问题描述 在Java应用程序中,当使用JDBC连接MySQL数据库时,由于Java和MySQL的时间格式不同,经常会出现时间不一致的问题,例如,数据库中的时间是2020-06-01 12:00:00,但在Java程序中读取时却变成了2020-06-01 08:00:00。 解决方法 为了解决Java和My…

    Java 2023年5月20日
    00
  • springboot 按月分表的实现方式

    使用SpringBoot分库分表组件的时候,要实现按月分表就需要按照下面的步骤进行配置。 步骤一:配置yml或properties文件 首先,我们需要在配置文件中设置分表相关的属性,例如: spring: sharding: tables: student: actual-data-nodes: ds$->{0..1}.student$->{20…

    Java 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部