Springboot使用MybatisPlus实现mysql乐观锁

 更新时间:2024年12月02日 08:23:48   作者:漫天转悠  
MySQL中的乐观锁(Optimistic Locking)是一种并发控制策略,本文将使用Springboot和MybatisPlus实现MySQL中的乐观锁,需要的可以参考下

1 什么是mysql的乐观锁

MySQL中的乐观锁(Optimistic Locking)是一种并发控制策略,它基于这样一个假设:数据冲突并不频繁发生,因此在读取数据时不会对数据加锁,而是在提交更新的时候才会正式对数据的冲突与否进行检测。如果发现冲突了,则让返回冲突信息,让用户决定如何去做下一步,比如重试或者回滚。乐观锁的核心思想是尽量减少锁定资源的时间,提高系统的并发性能,同时保证数据的一致性。

2 乐观锁的实现方式

乐观锁的实现通常有两种主要的方式:使用数据版本(Version)记录机制和时间戳机制。这两种方式都是为了确保在并发环境中,当多个事务试图修改同一份数据时,能够正确地处理这些请求,避免数据不一致的问题。

1. 使用数据版本(Version)记录机制

这是最常见的一种乐观锁实现方式。具体来说,就是在数据库表中增加一个数字类型的version字段来表示数据被修改的次数。当读取数据时,将version字段的值一同读出;在更新数据时,会检查当前记录的version值是否与之前读取的一致。如果一致,则更新成功,并将version值加1;如果不一致,则认为数据已经被其他事务修改,当前更新失败,通常会提示用户重新尝试。

2. 使用时间戳机制

另一种实现乐观锁的方式是使用时间戳(Timestamp)。这种方式与版本号类似,但使用的字段类型是时间戳。在更新提交的时候,系统会检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。这种方法的一个缺点是,当并发事务时间间隔小于当前系统平台的最小时间单位时,可能发生覆盖前一个事务结果的问题。

3 SpringBoot下实现乐观锁思路

在Spring Boot项目中使用MyBatis-Plus和MySQL实现乐观锁,主要是为了应对并发环境下的数据一致性问题。乐观锁是一种并发控制策略,它假设多个事务不会发生冲突,在执行操作时不加锁,非常乐观,只需每次提交时利用标识进行对比,确认其他事务没修改过即可提交。这种方式适用于读多写少的应用场景,因为它没有加锁,避免了锁竞争带来的性能消耗,所以吞吐量非常高。

本文将使用数据版本(Version)记录机制来对乐观锁进行实现。

注意:请确保已经引入了mybaitsplus和mysql和test测试依赖,以及成功配置连接上数据库,本文将不做依赖引入示例。

4 MyBatis-Plus中的乐观锁插件配置

MyBatis-Plus提供了现成的乐观锁插件OptimisticLockerInnerInterceptor,可以方便地集成到Spring Boot项目中。要启用这个插件,首先需要在项目的配置类中注册该插件。以下是具体的配置步骤:

4.1 数据库表设计

为了支持乐观锁,数据库表也需要相应地设计。通常是在表中添加一个version字段,用于记录数据的版本信息。例如,创建一个名为user的表,其中包含version字段:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `version` int(11) DEFAULT 1 COMMENT '数据版本号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在这个例子中,version字段的默认值设置为1,以便于初始化。

4.2 配置乐观锁插件

在Spring Boot的配置类中添加OptimisticLockerInnerInterceptor插件。可以通过@Bean注解的方式注入插件,如下所示:

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 注册乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

4.3 实体类中添加@Version注解

接下来,在实体类中为需要使用乐观锁的字段添加@Version注解。例如,对于一个用户实体类User,可以在version字段上添加此注解:

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

    // 乐观锁版本号
    @Version
    private Integer version;
}

这里需要注意的是,@Version注解支持的数据类型包括:int, Integer, long, Long, Date, Timestamp, LocalDateTime。整数类型下newVersion = oldVersion + 1,并且newVersion会回写到entity中。

4.4 测试乐观锁的效果

为了验证乐观锁是否生效,可以通过编写单元测试来模拟并发场景。例如,创建两个线程同时尝试更新同一条记录,观察更新的结果。如果其中一个线程更新成功,而另一个线程因为版本号不匹配而更新失败,则说明乐观锁已经正确实现。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class OptimisticLockTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testOptimisticLock() throws InterruptedException {
        // 线程1
        Thread thread1 = new Thread(() -> {
            User user1 = userMapper.selectById(1L);
            user1.setName("zhangsan");
            userMapper.updateById(user1);
        });

        // 线程2
        Thread thread2 = new Thread(() -> {
            User user2 = userMapper.selectById(1L);
            user2.setName("lisi");
            userMapper.updateById(user2);
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
    }
}

在这个测试中,thread1和thread2几乎同时启动,试图更新同一个用户的姓名。由于乐观锁的存在,只有其中一个线程能够成功更新数据,而另一个线程则会因为版本号不匹配而更新失败。

5 注意事项

版本号字段的默认值:建议将version字段的默认值设置为1,这样可以确保初次插入数据时版本号就已经存在。

update(entity, wrapper)方法下的wrapper不能复用:这意味着在构建查询条件时,应该为每个更新操作创建新的wrapper实例,以避免潜在的问题。

自定义SQL语句:如果使用了自定义的SQL语句进行更新操作,那么需要手动处理版本号的比较和更新,否则乐观锁将不会生效。

通过上述步骤,就可以在Spring Boot项目中使用MyBatis-Plus和MySQL实现乐观锁,从而有效地解决并发环境下的数据一致性问题。

到此这篇关于Springboot使用MybatisPlus实现mysql乐观锁的文章就介绍到这了,更多相关Springboot MybatisPlus实现乐观锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入探讨Java内存区域

    深入探讨Java内存区域

    本篇文章对Java内存区域的使用进行了详细的介绍,内容很全面,需要的朋友可以参考下
    2015-07-07
  • Java接口返回json如何忽略特定属性

    Java接口返回json如何忽略特定属性

    这篇文章主要介绍了Java接口返回json如何忽略特定属性,通过SimplePropertyPreFilter方式(Json返回),这种写法,接口返回类型就要求是Json字符串类型,本文通过场景实例代码相结合给大家介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • Java布隆过滤器的原理和实现分析

    Java布隆过滤器的原理和实现分析

    数组、链表、树等数据结构会存储元素的内容,一旦数据量过大,消耗的内存也会呈现线性增长所以布隆过滤器是为了解决数据量大的一种数据结构。本文就来和大家详细说说布隆过滤器的原理和实现,感兴趣的可以了解一下
    2022-10-10
  • Go Java算法之找不同示例详解

    Go Java算法之找不同示例详解

    这篇文章主要为大家介绍了Go Java算法之找不同示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • SpringCloud+Redis实现Api接口限流防止恶意刷接口

    SpringCloud+Redis实现Api接口限流防止恶意刷接口

    接口限流是为了保护系统和服务,防止因为过多的请求而崩溃,本文主要介绍了SpringCloud+Redis实现Api接口限流防止恶意刷接口,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Java split函数拆分后变成null问题解决方案

    Java split函数拆分后变成null问题解决方案

    这篇文章主要介绍了Java split函数拆分后变成null问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • 智能 AI 代码生成工具 Cursor 安装和使用超详细教程

    智能 AI 代码生成工具 Cursor 安装和使用超详细教程

    Cursor.so 是一个集成了 GPT-4 的国内直接可以访问的,优秀而强大的免费代码生成器,可以帮助你快速编写、编辑和讨论代码,这篇文章主要介绍了智能 AI 代码生成工具 Cursor 安装和使用介绍,需要的朋友可以参考下
    2023-05-05
  • SpringBoot3.1.2 引入Swagger报错Type javax.servlet.http.HttpServletRequest not present解决办法

    SpringBoot3.1.2 引入Swagger报错Type javax.servlet.http

    这篇文章主要介绍了SpringBoot3.1.2 引入Swagger报错Type javax.servlet.http.HttpServletRequest not present解决办法,文中通过代码示例给大家介绍的非常详细,需要的朋友可以参考下
    2024-03-03
  • 快速上手Java单元测试框架JUnit5

    快速上手Java单元测试框架JUnit5

    今天给大家带来的是关于Java单元测试的相关知识,文章围绕着Java单元测试框架JUnit5展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • Java俄罗斯方块小游戏

    Java俄罗斯方块小游戏

    这篇文章主要为大家详细介绍了Java俄罗斯方块小游戏,实现了俄罗斯的经典功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08

最新评论