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框架和快速入门

    下面我就来详细讲解“初识SpringBoot框架和快速入门”的完整攻略。 一、什么是Spring Boot? Spring Boot是一个开源的框架,它是基于Spring 框架的基础上创建的一个快速开发的框架。它封装了大量的Spring框架相关的组件和工具,简化了Spring应用的初始化和开发过程,大大提高了开发效率和开发体验。 二、Spring Boot的…

    Java 2023年5月15日
    00
  • spring-cloud-stream的手动消息确认问题

    Spring Cloud Stream是一个用于构建基于事件驱动的微服务的框架。可使用其发现和连接分布式系统中的消息代理,同时提供一些便捷的特性。 在使用Spring Cloud Stream的过程中,手动消息确认是重要的一个问题。手动确认就是指当我们消费了消息后需要向消息队列发送一个确认消息来告诉队列已经处理完消息,可以将消息从队列中删除。否则,队列会一直…

    Java 2023年6月2日
    00
  • 浅谈SpringBoot优化技巧

    SpringBoot优化技巧 SpringBoot是目前广泛应用于Java web开发中的一款优秀框架,其简化了开发流程、提高了开发效率、提升了代码的可维护性,在实际开发中应用广泛。但是,一些不良操作或者技术栈的选择不当,会导致性能问题出现。 为了解决这些问题,我们需要对SpringBoot进行优化。在本文中,我将详细介绍一些SpringBoot的优化技巧,…

    Java 2023年5月15日
    00
  • 利用apache ftpserver搭建ftp服务器的方法步骤

    当您想要在本地或远程计算机上快速共享文件时,FTP服务器是一种非常有用的工具。Apache FTP服务器是一个优秀的FTP软件,拥有强大的安全功能,易于配置。 以下是利用Apache FTP服务器搭建FTP服务器的步骤,包括Linux和Windows系统。 在Linux上安装Apache FTP服务器 首先,确保Java已经安装。可以在命令行中运行 java…

    Java 2023年6月2日
    00
  • 基于Spring框架的Shiro配置方法

    基于Spring框架的Shiro配置方法 简介 Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了身份认证、授权、加密等安全功能。Spring框架与Shiro框架完美结合可以非常方便地实现网站的安全控制。本文将介绍使用Spring框架来配置Shiro框架的方法。 环境准备 在进行配置之前,我们需要先在项目中添加Shiro和Spring…

    Java 2023年6月3日
    00
  • Java的JNI快速入门教程(推荐)

    Java的JNI快速入门教程 什么是JNI? JNI,全称Java Native Interface,是Java平台提供的用于实现Java与其他语言之间互操作性的一个机制。通过JNI,Java程序可以调用native方法实现与C/C++等本地语言代码的交互,也可以被其他语言调用。 JNI的基本原理 JNI的本质是在Java虚拟机与本地代码之间建立通信桥梁。调…

    Java 2023年5月23日
    00
  • JAVA内部类示例详解及练习

    下面我就来详细讲解一下“JAVA内部类示例详解及练习”的完整攻略。 什么是Java内部类 Java内部类(Inner Class)指的是定义在另一个类中的类。Java内部类可以分为四种类型:成员内部类、静态内部类、局部内部类、匿名内部类。其中,成员内部类是最常用的一种形式。 成员内部类示例 下面通过一个示例来详解一下成员内部类的定义和使用: public c…

    Java 2023年5月23日
    00
  • SpringMVC框架实现图片上传与下载

    下面是关于“SpringMVC框架实现图片上传与下载”的完整攻略,包含两个示例说明。 SpringMVC框架实现图片上传与下载 SpringMVC是一个流行的Java Web框架,它可以帮助我们更加方便地构建Web应用程序。本文将介绍如何使用SpringMVC框架实现图片上传与下载。 步骤一:创建SpringMVC项目 首先,我们需要创建一个SpringMV…

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