MyBatis实现乐观锁和悲观锁

2023-11-10

使用mysql做数据库,mybatis做orm的系统中,mybatis的乐观锁和悲观锁实际上就是mysql的乐观锁和悲观锁。

实例中使用springboot整合mybatis,一并记录了。

添加依赖:

<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>

application.properties配置:

mybatis.type-aliases-package=com.lin.learn.mysql

spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://192.168.0.103:3306/mysql?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root

实体类:

public class TestEntity {

    private int id;

    private int count;

    private int version;

    public int getId() {
        return id;
    }

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

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getVersion() {
        return version;
    }

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

加上相关注解:

@SpringBootApplication
@MapperScan("com.lin.learn.mysql")
public class App {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(App.class, args);
    }
}

乐观锁

就是使用一个version字段标识数据的当前版本,每次更新数据的时候同时更新version = version + 1,where条件中需要加上version等于当前事务查询出的数据的version,如果version的值已经改变,则更新失败。

Mapper:

public interface TestMapper {

    @Select("select * from `test` where `id` = #{id} for update")
    @Results({
            @Result(column = "id", property = "id", javaType = Integer.class),
            @Result(column = "count", property = "count", javaType = Integer.class),
            @Result(column = "version", property = "version", javaType = Integer.class)
    })
    public TestEntity getById(@Param("id") int id);


    @Update("update `test` set `count` = #{count}, `version` = #{version} + 1 where `id` = #{id} and `version` = #{version}")
    public int update(TestEntity testEntity);
}

业务代码,就是数据库一个数据修改:

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Component
public class TestService {

    @Resource
    private TestMapper mapper;

    public void increaseCount(int id) {

        while (true) {
            try {
                TestEntity testEntity = mapper.getById(id);
                testEntity.setCount(testEntity.getCount() + 1);
                int updateCount = mapper.update(testEntity);
                if(updateCount > 0) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

测试:

import com.lin.learn.mysql.TestService;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
@MapperScan("com.lin.learn.mysql")
public class App {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(App.class, args);

        TestService service = applicationContext.getBean(TestService.class);

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseCount(3);
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseCount(3);
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseCount(3);
                }
            }
        }).start();
    }
}

上面代码执行后数据库数据最终结果得到正确的数据。

悲观锁

本质上就是用select for update锁行,需要注意的是如果where子句条件没有命中索引将导致锁表。并且查询和更新操作都需要在同一个事务里里面。

在Mapper添加新的更新方法:

import org.apache.ibatis.annotations.*;

public interface TestMapper {

    @Select("select * from `test` where `id` = #{id} for update")
    @Results({
            @Result(column = "id", property = "id", javaType = Integer.class),
            @Result(column = "count", property = "count", javaType = Integer.class),
            @Result(column = "version", property = "version", javaType = Integer.class)
    })
    public TestEntity getById(@Param("id") int id);


    @Update("update `test` set `count` = #{count}, `version` = #{version} + 1 where `id` = #{id} and `version` = #{version}")
    public int update(TestEntity testEntity);

    @Update("update `test` set `count` = #{count} where `id` = #{id}")
    public void updateNoSafe(TestEntity testEntity);
}

在业务代码里面添加新的方法:

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Component
public class TestService {

    @Resource
    private TestMapper mapper;

    public void increaseCount(int id) {

        while (true) {
            try {
                TestEntity testEntity = mapper.getById(id);
                testEntity.setCount(testEntity.getCount() + 1);
                int updateCount = mapper.update(testEntity);
                if(updateCount > 0) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @Transactional
    public void increaseNoSafe(int id) {

        try {
            TestEntity testEntity = mapper.getById(id);
            testEntity.setCount(testEntity.getCount() + 1);
            mapper.updateNoSafe(testEntity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里是用@Transaction注解声明这个更新数据的事务。

测试:

import com.lin.learn.mysql.TestService;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

@SpringBootApplication
@MapperScan("com.lin.learn.mysql")
public class App {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(App.class, args);

        TestService service = applicationContext.getBean(TestService.class);

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseNoSafe(3);
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseNoSafe(3);
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    service.increaseNoSafe(3);
                }
            }
        }).start();
    }
}

最终结果能得到正确的数据。

转载于:https://my.oschina.net/ChiLin/blog/3062497

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MyBatis实现乐观锁和悲观锁 的相关文章

  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 如何循环遍历所有组合,例如48 选择 5 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在java中从大小为n的集合中迭代生成k个元素子集 https stackoverflow com questions 4504974 how to iteratively generate k
  • 为什么 JTables 使 TableModel 在呈现时不可序列化?

    所以最近我正在开发一个工具 供我们配置某些应用程序 它不需要是什么真正令人敬畏的东西 只是一个具有一些 SQL 脚本生成功能并创建几个 XML 文件的基本工具 在此期间 我使用自己的 AbstractTableModel 实现创建了一系列
  • .properties 中的通配符

    是否存在任何方法 我可以将通配符添加到属性文件中 并且具有所有含义 例如a b c d lalalala 或为所有以结尾的内容设置一个正则表达式a b c anything 普通的 Java 属性文件无法处理这个问题 不 请记住 它实际上是
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 如何更改javaFX中按钮的图像?

    我正在使用javaFX 我制作了一个按钮并为此设置了图像 代码是 Image playI new Image file c Users Farhad Desktop icons play2 jpg ImageView iv1 new Ima
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • 从最终实体获取根证书和中间证书

    作为密码学的菜鸟 我每天都会偶然发现一些简单的事情 今天只是那些日子之一 我想用 bouncy castle 库验证 java 中的 smime 消息 我想我几乎已经弄清楚了 但此时的问题是 PKIXparameters 对象的构建 假设我
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • Eclipse 启动时崩溃;退出代码=13

    I am trying to work with Eclipse Helios on my x64 machine Im pretty sure now that this problem could occur with any ecli
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 如何防止在Spring Boot单元测试中执行import.sql

    我的类路径中有一个 import sql 文件 其中包含一些 INSERT 语句 当使用 profile devel 运行我的应用程序时 它的数据被加载到 postgres 数据库中 到目前为止一切正常 当使用测试配置文件执行测试时 imp
  • Spring Boot 无法更新 azure cosmos db(MongoDb) 上的分片集合

    我的数据库中存在一个集合 documentDev 其分片键为 dNumber 样本文件 id 12831221wadaee23 dNumber 115 processed false 如果我尝试使用以下命令通过任何查询工具更新此文档 db
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐

  • 数据结构:面试题目-专项练习-栈-习题练习

    1 下列关于栈叙述正确的是 正确答案 D 你的答案 D 正确 算法就是程序 设计算法时只需要考虑数据结构的设计 设计算法时只需要考虑结果的可靠性 以上三种说法都不对 解析 A 程序是数据结构 算法 错 B C 设计一个算法时 考虑的因素很多
  • 迅为i.MX6ULL开发板Platform设备驱动运行测试

    文章目录 1 编译驱动和设备程序 2 编译应用测试程序 3 运行测试 1 编译驱动和设备程序 和前面章节中驱动测试程序一样需要一个Makefile文件 只是将obj m的值改为led device o led driver o Makefi
  • vue项目初始化出现tar ENOENT: no such file or directory错误的解决办法。

    在npm install时 出现了tar ENOENT no such file or directory报错 原因 node的版本问题 解决办法 全局环境下更新node版本 或者使用公司内部包装过后的的 node 按照顺序运行下面的语句
  • 认识磁盘阵列柜性能

    一个 SCSI 硬盘的平均故障间隔时间 MTBF Mean Time Between Failure 都在数万 小时以上 在正常使用情况下 要坏掉一个硬盘已经很不容易了 在同一系统内 两个磁 盘驱动器同时坏掉的机率 更是微乎其微 但是 如果
  • Python中 ddt 数据驱动的小细节

    文章目录 前言 一 什么是DDT 二 安装ddt 三 基本原理和用法 总结 前言 记录ddt用法的一些小细节 一 什么是DDT DDT是 Data Driven Tests 的缩写 数据驱动测试 既然是测试 那么就要与单元测试框架一起使用
  • IDEA 4种解决控制台中文乱码问题

    前言 IntelliJ IDEA 如果不进行配置的话 运行程序时控制台中文乱码问题会非常严重 严重影响我们对信息的获取和程序的跟踪 我总结以下 4 点用于解决控制台中文乱码问题 希望有助于大家 注意 下面根据我日常工作的经验总结 排序的先后
  • 什么是吉布斯采样(Gibbs Sampling)

    目录 1 蒙特卡洛方法 1 1 蒙特卡洛方法的作用 1 2 非均匀分布采样 1 3 分布p x 不好采样怎么办 2 什么是吉布斯采样 2 1 马尔可夫链 2 1 1 什么是马尔可夫链呢 2 1 2 为什么我们要引入马尔可夫链 2 1 3 对
  • 快速学会git版本管理——创建分支和合并分支

    首先创建分支 git创建分支只需要使用switch 命令 git switch c 分支名 创建分支并切换到该分支 大家看后面的括号里已经变成了dev 说明我们切换成功了 然后想要合并分支就在 创建的分支中 进行提交修改的内容 还是通过 a
  • 5G QoS控制原理专题详解-QoS控制原理(2)

    相关文章会在公众号同步更新 最近工作忙 更新完公众号后 经常容易忘记再CSDN上再发 公众号上的文章更新的能快一些 各位同学有兴趣可以关注一下 公众号 5G通信大家学 持续更新的相关5G内容都是直接根据3GPP整理 保证更新内容的准确性 避
  • OOM(内存溢出)造成原因及解决方案

    一 概念 内存溢出 Out Of Memory 简称OOM 是指应用系统中存在无法回收的内存或使用的内存过多 最终使得程序运行要用到的内存大于能提供的最大内存 二 造成原因 2 1 内存泄漏 由于长期保持某些资源的引用 垃圾回收器无法回收它
  • first season seventh episode,The entire city is blacked out!(全城大停电,Chandler有好事???)

    Scene Central Perk Rachel is introducing Phoebe who is playing her guitar for the crowd Rachel Everybody Shh shhh Uhhh C
  • 【Java项目】多文件传输

    1 前言 为了实现一个C S框架应用 客户端向服务器请求资源 采用传统的BIO模式传输 问题 但当客户端数量增加 传输效率下降 尤其是多个客户端请求1G文件时 传输大概有十多分钟 问题分析 客户端所需要的资源都是通过服务器单独发送 当请求增
  • Angular6项目构建

    Angular6项目构建 安装Nodejs长期支持版本 LTS 设置npm淘宝代理 npm config set registry https registry npm taobao org npm install g angular cl
  • linux 使用chomd给文件授权 报错 -bash: chomd: 未找到命令

    1 简介 chmod是一个Linux Unix命令 用于更改文件或目录的权限 2 解决方案 以下是一些常见的解决方案 输入命令时检查有无拼写错误 确认您是否具有执行该命令所需的权限 检查您的系统是否已安装此命令 您可以尝试使用whereis
  • 忘记iPhone锁屏密码?三种方法帮你顺利解决!

    不论是苹果手机用户还是安卓手机用户 如果开机的锁屏密码忘记的话 应该是比较麻烦的一件事情 iPhone用户如果连续尝试6次密码都不对的话 iPhone将会自动锁死 也就变成了我们常说的 砖机 不过在iPhone锁死之前 我们还有三种方法可以
  • 在WindowsXP系统下搭建PPPoE服务器总结

    对与路由器的测试中遇到一个问题就是PPPoE的测试 具体如下 一 组网图如下 二 要确保WindowsXP系统的服务中Workstation Telephony Remote Access Connection Manager Routin
  • 已获万赞,最新Android通用流行框架大全

    前言 2021全年从开头到结尾似乎就没多少好消息 这里我说的是整个互联网行业 并没有单单挑出某个公司或者某个细分领域 而对于广大Android开发者来说 找工作似乎也变得越来越难了 2021年1月 我在51job搜索了一下北京地区的Andr
  • 基于STM32 的IIC 模拟主机编写

    最近在学习STM32 的IIC IO 模拟主机方式 好像失败了 include myiic h include delay h MCU VERSION DATA 作者 other Function IIC
  • 吴恩达深度学习笔记五:卷积神经网络 人脸识别和风格迁移部分

    1 人脸识别 人脸验证 Face Verification 输入图片和模板图片是否为同一人 一对一问题 人脸识别 Face Recognition 输入图片 检测是否为多个模板图片中的一个 一对多问题 一般来说 人脸验证由于范围较小难度较小
  • MyBatis实现乐观锁和悲观锁

    使用mysql做数据库 mybatis做orm的系统中 mybatis的乐观锁和悲观锁实际上就是mysql的乐观锁和悲观锁 实例中使用springboot整合mybatis 一并记录了 添加依赖