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日

相关文章

  • 详解Spring boot/Spring 统一错误处理方案的使用

    详解Springboot/Spring统一错误处理方案的使用 在SpringBoot/Spring应用程序中,错误处理是一个非常重要的方面。在本文中,我们将介绍如何使用SpringBoot/Spring的统一错误处理方案来处理应用程序中的错误。 步骤一:添加依赖 我们需要在pom.xml文件中添加Spring Boot Starter Web依赖。以下是一个…

    Java 2023年5月15日
    00
  • jsp+servlet+jdbc实现对数据库的增删改查

    首先,我们需要明确一下技术栈的概念: JSP (Java Server Pages):基于Java语言的服务器端网页开发技术; Servlet:是Java Web应用程序的核心,位于服务端,负责处理浏览器发送过来的HTTP请求并返回响应结果; JDBC (Java Database Connectivity):Java数据库连接,Java语言操作关系型数据库…

    Java 2023年5月20日
    00
  • 详解java数组进行翻转的方法有哪些

    详解Java数组进行翻转的方法有哪些 Java中提供了多种翻转数组的方法,可以通过修改数组元素的顺序或者创建新数组来实现。本文将为大家介绍四种常用的翻转数组的方法。 1. 利用for循环实现 public static int[] reverseArray(int[] array) { int length = array.length; int[] res…

    Java 2023年5月26日
    00
  • java实现简单的学生信息管理系统代码实例

    针对“java实现简单的学生信息管理系统代码实例”的完整攻略,以下是详细讲解过程: 1.准备工作 首先,我们要在本地安装Java开发环境(Java Development Kit,简称JDK),可以从Oracle官网上下载并安装最新版JDK,安装过程中需要设置环境变量,以便于后续命令行操作。 2.创建项目 接下来,我们创建项目文件夹来存放程序代码和相关文件,…

    Java 2023年5月23日
    00
  • SpringBoot测试junit遇到的坑及解决

    下面是“SpringBoot测试junit遇到的坑及解决”的完整攻略。 一、问题描述 在使用SpringBoot进行junit测试时,可能会遇到一些困难和坑,如: 无法注入bean到测试类中 难以模拟controller层中的请求 这些问题可能会导致测试失败,影响开发效率。因此,我们需要找到解决方案。 二、解决方案 1. 解决bean注入失败的问题 在测试类…

    Java 2023年5月19日
    00
  • Tomcat服务器的安装配置图文教程(推荐)

    下面详细讲解“Tomcat服务器的安装配置图文教程(推荐)”的完整攻略。 1. 下载与安装Tomcat 首先,从Tomcat官网 https://tomcat.apache.org/ 下载最新的Tomcat安装文件,选择与你系统对应的版本(一般会选择zip或tar.gz压缩文件)。下载完成后,将Tomcat文件解压到你想要安装的目录中。 示例: # 假设我们…

    Java 2023年5月19日
    00
  • Java多态的全面系统解析

    Java多态的全面系统解析 什么是多态 多态(Polymorphism)是面向对象编程中一个非常重要的概念,指的是同类对象的不同表现形式。具体而言,多态是指在运行时根据实际类型来确定对象的实际行为。 Java中的多态可以分为两种:编译时多态和运行时多态。 编译时多态,也称为静态多态,是指在编译时就能确定具体的方法调用。这种多态是通过Java的方法重载实现的。…

    Java 2023年5月23日
    00
  • Java日常练习题,每天进步一点点(18)

    让我来详细讲解一下“Java日常练习题,每天进步一点点(18)”的完整攻略。该攻略是一个Java练习题,旨在帮助大家每天都可以进步一点点。 首先,大家需要先准备好Java环境,通过编写代码来完成练习题。下面是该攻略的主要步骤: 阅读题目并理解题意。 使用Java语言编写代码。 运行代码并测试调试。 检查代码是否符合题目要求。 下面是两个示例说明: 示例1:要…

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