Mybatis Plus乐观锁实现

news2024/9/21 2:33:14

1 引言

乐观锁的主要作用在于提高系统的并发性能和减少锁冲突,同时保证数据的一致性。‌其原理简单来说就是,在修改数据的时候,判断数据是否被其他人改过,如果已被其他人改过,则修改失败。

2 代码

在SpringBoot 3.x+Mybatis Plus多数据源极简配置中的项目基础上更新代码,下面列出需要更新的代码。
在这里插入图片描述

2.1 org/example/mapper/InventoryMapperMaster.java

package org.example.mapper;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.entity.Inventory;

@DS("master")
public interface InventoryMapperMaster extends BaseMapper<Inventory> {


}

2.2 org/example/entity/Inventory.java

@Version就是乐观锁字段,用于判断数据是否被修改过。

package org.example.entity;

import com.baomidou.mybatisplus.annotation.*;

import java.util.Date;

/*
 * 库存
 * */
@TableName("t_inventory")
public class Inventory {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer quantity;
    @Version
    @TableField(fill = FieldFill.INSERT)
    private Integer version;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

2.3 org/example/controller/InventoryController.java

package org.example.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.entity.Inventory;
import org.example.entity.User;
import org.example.mapper.InventoryMapperMaster;
import org.example.mapper.UserMapperMaster;
import org.example.mapper.UserMapperSlave;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Controller
@RequestMapping("/InventoryController")
public class InventoryController {


    @Autowired
    private InventoryMapperMaster inventoryMapperMaster;


    @RequestMapping(value = "/insert")
    @ResponseBody
    @Transactional
    public Object insertMaster() {
        for (int i = 0; i < 5; i++) {
            Inventory inventory = new Inventory();
            inventory.setName("商品" + i);
            inventory.setQuantity(10);
            inventoryMapperMaster.insert(inventory);
        }
        return "数据插入成功";
    }

    @RequestMapping(value = "/update1")
    @ResponseBody
    @Transactional
    public Object update1() {
        QueryWrapper<Inventory> q = new QueryWrapper<>();
        q.eq("name", "商品1");
        Inventory inventory = inventoryMapperMaster.selectOne(q);
        inventory.setQuantity(5);
        try {
            Thread.sleep(10000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        int i = inventoryMapperMaster.updateById(inventory);
        System.out.println("update1更新:" + i + "条");
        return "数据修改成功";
    }


    @RequestMapping(value = "/update2")
    @ResponseBody
    @Transactional
    public Object update2() {
        QueryWrapper<Inventory> q = new QueryWrapper<>();
        q.eq("name", "商品1");
        Inventory inventory = inventoryMapperMaster.selectOne(q);
        inventory.setQuantity(0);
        int i = inventoryMapperMaster.updateById(inventory);
        System.out.println("update2更新:" + i + "条");
        return "数据修改成功";
    }

}

2.4 org/example/conf/MyMetaObjectHandler.java

package org.example.conf;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

/*
 MyBatis-Plus 提供了一个便捷的自动填充功能,用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("version", Integer.valueOf("0"), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

2.5 org/example/conf/MybatisPlusConfig.java

package org.example.conf;

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

/*
 MyBatis-Plus 配置
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁拦截器
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }

}

3 测试

3.1 创建库存表

CREATE TABLE `t_inventory` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `name` varchar(30) DEFAULT NULL COMMENT '名称',
  `quantity` int(11) DEFAULT NULL COMMENT '数量',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '修改时间',
  `version` int(11) DEFAULT NULL COMMENT '版本',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3.2 新增数据

启动项目,然后打开浏览器访问:http://localhost:8080/InventoryController/insert,可以看到当前所有商品的库存数量都是10个。
在这里插入图片描述

3.3 修改数据

  1. 我们以“商品1”为例,先访问http://localhost:8080/InventoryController/update1,将“商品1”的数量改为5。然后立即(10秒内)访问:http://localhost:8080/InventoryController/update2,将“商品1”的数量改为0。为了模拟并发的情况,这里让update1休眠了10秒钟再提交。
    在这里插入图片描述
  2. 可以看到sql中自动加入了version=0的条件,这个version就是乐观锁,由系统自动维护,每次成功修改数据自动+1。具体流程是:
    2.1 update1 首先查询商品1,这时quantity=10,version=0,查完进入休眠。
    2.2 update2 在update1 休眠期间,将商品1的数量改为0,这时version自动+1,变成quantity=0,version=1。
    2.3 update1 休眠结束后提交更新,仍然按照version=0的条件更新数据,此时version=0的数据已经不存在了,因此可以看到update1更新了0条数据,避免了数据不一致的情况。
    在这里插入图片描述

5 完整代码

Gitee代码链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2079322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

掏耳勺买什么样的好?五大超值不踩雷可视挖耳勺推荐种草!

可视挖耳勺是热度特别高的个人清洁工具&#xff0c;不仅能够更加清晰地观察自己耳内的状况&#xff0c;从而更加安全、有效地清洁耳朵&#xff0c;可以发挥多种多样的作用&#xff0c;但是市面上也隐藏了很多劣质不专业产品&#xff0c;使用这类产品容易出现成像效果模糊、机身…

AI文案新纪元:用ChatGPT构建你的文案创作系统

文章目录 一、ChatGPT在文案创作中的独特优势1. 高效生成&#xff0c;快速响应市场2. 个性化定制&#xff0c;精准触达受众3. 创新表达&#xff0c;提升内容吸引力 二、构建文案创作系统的详细步骤1. 需求分析与系统规划2. 接入ChatGPT API与集成开发3. 设计用户友好的交互界面…

集合及数据结构第十三节(下)———— 枚举与Lambda表达式

系列文章目录 集合及数据结构第十三节&#xff08;下&#xff09;———— 枚举与Lambda表达式 枚举与枚举的使用 什么是C语言数据类型变量、常量字符串转义字符注释选择语句循环语句函数数组操作符常见关键字define 定义常量和宏指针结构体 文章目录 系列文章目录集合及数据…

USB安全存储专家|有效控制U盘读写权限、排名前5的USB安全存储工具大公开啦!

在有效控制U盘读写权限方面&#xff0c;USB安全存储专家及其相关工具扮演着至关重要的角色。 以下是排名前5的USB安全存储工具&#xff0c;它们在保护数据安全、控制U盘读写权限方面表现出色。 1. 安企神 简介&#xff1a;它是一款专注于终端安全管控的软件&#xff0c;具备强…

为你揭秘个股场外期权的盈利模式!

今天带你了解为你揭秘个股场外期权的盈利模式&#xff01;场外个股期权作为一种灵活的金融工具&#xff0c;为投资者提供了多种盈利途径。个股场外期权的盈利模式主要依赖于标的资产价格的变动。 这里是几种常见的个股场外期权盈利方式&#xff1a; 1.看涨期权&#xff1a; …

京存分布式赋能EDA应用

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。——《老子德经第六十四章》 EDA&#xff08;Electronic Design Automation 电子设计自动化&#xff09;是利用计算机&#xff0c;完成对VLSI &#xff08;V…

vivo手机短信删除了怎么恢复?恢复办法分享

在日常使用vivo手机的过程中&#xff0c;我们可能会不小心删除了重要的短信&#xff0c;这往往让人感到焦急和不安。但不必过于担心&#xff0c;因为vivo手机提供了多种途径来恢复被删除的短信。本文将为您详细介绍几种恢复vivo手机短信的方法&#xff0c;帮助您快速找回重要信…

创业旅途之---变频器开发

最近做一个项目&#xff0c;是开发一款变频器&#xff1b;准备采用Free RTOS&#xff0c;突然发现好像还不知道这是个啥&#xff0c;基于多年“windows的操作经验”—管不了那么多了&#xff0c;必须把它用上&#xff0c;开干---- 看书使人懵逼&#xff0c;实践才是王道&#x…

【Hot100】LeetCode—994. 腐烂的橘子

目录 1- 思路BFSQueue队列 2- 实现⭐994. 腐烂的橘子——题解思路 3- ACM 实现 题目连接&#xff1a;994. 腐烂的橘子 1- 思路 BFSQueue队列 思路&#xff1a;先 ①遍历存储腐烂橘子的下标、② ① 队列的作用&#xff1a;Queue<int[]> queue new LinkedList<>…

如何在没有屏幕时间密码或 Apple ID 的情况下重置 iPhone

忘记屏幕时间密码或 Apple ID 可能会令人沮丧&#xff0c;尤其是当您尝试重置 iPhone 时。无论您是出售设备、传承设备&#xff0c;还是只需要重新开始&#xff0c;知道如何在没有屏幕时间密码的情况下重置 iPhone都是必不可少的。 幸运的是&#xff0c;有几种方法可以帮助您在…

基于STM32开发的智能家居语音控制系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化语音识别处理设备控制与状态显示Wi-Fi通信与远程控制应用场景 家庭环境的语音控制办公室的智能化管理常见问题及解决方案 常见问题解决方案结论 1. 引言 随着人工智能技术的发展&…

Node-RED订阅巴法云的数据并展示

目录 订阅巴法云数据 添加 MQTT订阅 添加模板 使用MQTTX模拟数据产生 调试信息 使用Dashboard进行数据展示 添加Dashboard模块 最终展示效果 在前面的博文Node-RED的安装-CSDN博客和使用Node-RED发送数据到巴法云-CSDN博客&#xff0c;我已经介绍了Node-RED的简单用法&…

博弈论(Nim游戏的扩展)

公平组合游戏ICG 若一个游戏满足: 1.由两名玩家交替行动; 2.在游戏进程的任意时刻&#xff0c;可以执行的合法行动与轮到哪名玩家无关; 3.不能行动的玩家判负; 则称该游戏为一个公平组合游戏。 NIM博弈属于公平组合游戏&#xff0c;但城建的棋类游戏&#xff0c;比如围棋&…

【Python机器学习】NLP分词——词的“情感”

目录 VADER&#xff1a;一个基于规则的情感分析器 朴素贝叶斯 无论NLP流水线中使用的是单个词、n-gram、词干还是词元作为词条&#xff0c;每个词条都包含了一些信息&#xff0c;这些信息中一个重要部分是词的情感&#xff0c;即一个词所唤起的总体感觉或感情。这种度量短语或…

XOR 已知明文攻击

在这篇博文中,我们详细展示了*针对 XOR 编码的*已知明文攻击是如何进行的,并使用自定义工具自动执行该攻击,以解密和提取 Cobalt Strike 信标的配置。如果您对理论不感兴趣,只对工具感兴趣,请直接跳到结论! 已知明文攻击 (KPA) 是一种密码分析方法,分析人员拥有消息的明文…

【设计模式-装饰】

定义 装饰模式是一种结构型设计模式&#xff0c;它允许在不改变对象自身的前提下动态地给对象添加职责&#xff08;功能&#xff09;。通过使用装饰模式&#xff0c;可以将对象的功能扩展变得更加灵活和可维护&#xff0c;同时避免了类继承的复杂性。 特点 动态扩展对象功能…

猫咪浮毛满天飞?希喂、IAM、352三款宠物空气净化器测试对比

我作为一名养宠三年的铲屎官&#xff0c;用过不少清理工具&#xff0c;可惜都效果不佳。经过朋友的推荐&#xff0c;我了解到宠物空气净化器这种产品&#xff0c;想要购入使用。在挑选时我发现&#xff0c;目前市面上宠物空气净化器品牌繁多&#xff0c;质量难辨。因此&#xf…

【iOS端】基于Uniapp跨平台接入即构RTC+相芯美颜

0 Uniapp平台接入完成iOS端的即构RTC相芯美颜 Uniapp最大优势是跨平台&#xff0c;前面介绍了如何在android中接入相芯美颜即构RTC&#xff0c;今天咱们把相同的代码接入到iOS中。按照惯例我们先看最终效果&#xff0c;欢迎大家评论讨论&#xff1a; 1 开发配置准备 iOS开发因…

YOLOv8目标检测部署RK3588全过程,附代码pt->onnx->rknn,附【详细代码】

目录 一、训练yolov8模型&#xff0c;得到最佳权重文件best.pt 二、pt转onnx,即best.pt->best11.onnx 1、对下载的YOLOv8代码修改 2、加入模型导出功能&#xff0c; 3、导出指令采用如下代码 三、ONNX转RKNN 四、RK3588部署 1、拷贝rknn文件到rk3588板子内 2、执行…

.NET6 多环境 在Windows IIS部署的应用场景

前言&#xff1a; 通常情况下 我们部署Web站点 需要用到 测试环境 和 正式环境 2个环境 Staging环境 或者其他环境 视 团队所处阶段、团队规模、质量要求、风险控制要求、第3方协议而定 1.我们把上一讲 IX.MultiEnvironment 这个项目发布到IIS中 2.IIS配置3个环境 我们重点…