MySQL使用版本号实现乐观锁
原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11608581.html
乐观锁适用于读多写少的应用场景
乐观锁Version图示

Project Directory

Maven Dependency
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>HelloSpring</groupId>
<artifactId>org.fool.spring</artifactId>
<version>1.0-SNAPSHOT</version> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.22.RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</exclusion>
</exclusions>
</dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.5</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>sql/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
server.port= spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver mybatis.type-aliases-package=org.fool.spring.model
mybatis.mapper-locations=classpath:mapper/**/*.xml
ddl.sql
CREATE TABLE `goods` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '商品名称',
`state` tinyint(4) unsigned NOT NULL COMMENT '1.正常;2.缺货',
`version` bigint(20) unsigned NOT NULL,
`create_time` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`update_time` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; insert into goods(name, state, version) values('iPhone', 1, 1);
insert into goods(name, state, version) values('iMac', 1, 1);
insert into goods(name, state, version) values('iPad', 1, 1);
insert into goods(name, state, version) values('iWatch', 1, 1);
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry
location="/Users/${user.name}/.m2/repository/mysql/mysql-connector-java/5.1.48/mysql-connector-java-5.1.48.jar"/> <context id="test" targetRuntime="MyBatis3"> <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/> <commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator> <jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/test"
userId="root"
password="123456">
</jdbcConnection> <javaModelGenerator targetPackage="org.fool.spring.model" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
</javaModelGenerator> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator> <javaClientGenerator targetPackage="org.fool.spring.dao.mapper" targetProject="src/main/java"
type="XMLMAPPER">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator> <table tableName="goods" domainObjectName="Goods"
enableCountByExample="false" enableUpdateByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false">
</table> </context> </generatorConfiguration>
自动生成model和mapper
mybatis-generator:generate -e
Goods.java
package org.fool.spring.model;
import java.util.Date;
public class Goods {
private Long id;
private String name;
private Byte state;
private Long version;
private Date createTime;
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 == null ? null : name.trim();
}
public Byte getState() {
return state;
}
public void setState(Byte state) {
this.state = state;
}
public Long getVersion() {
return version;
}
public void setVersion(Long 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;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", name=").append(name);
sb.append(", state=").append(state);
sb.append(", version=").append(version);
sb.append(", createTime=").append(createTime);
sb.append(", updateTime=").append(updateTime);
sb.append("]");
return sb.toString();
}
}
GoodsMapper.java
package org.fool.spring.dao.mapper;
import org.fool.spring.model.Goods;
public interface GoodsMapper {
int deleteByPrimaryKey(Long id);
int insert(Goods record);
int insertSelective(Goods record);
Goods selectByPrimaryKey(Long id);
int updateByPrimaryKeySelective(Goods record);
int updateByPrimaryKey(Goods record);
}
GoodsMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.fool.spring.dao.mapper.GoodsMapper">
<resultMap id="BaseResultMap" type="org.fool.spring.model.Goods">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="state" jdbcType="TINYINT" property="state" />
<result column="version" jdbcType="BIGINT" property="version" />
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime" />
</resultMap>
<sql id="Base_Column_List">
id, name, state, version, create_time, update_time
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from goods
where id = #{id,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from goods
where id = #{id,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="org.fool.spring.model.Goods">
insert into goods (id, name, state,
version, create_time, update_time
)
values (#{id,jdbcType=BIGINT}, #{name,jdbcType=VARCHAR}, #{state,jdbcType=TINYINT},
#{version,jdbcType=BIGINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
)
</insert>
<insert id="insertSelective" parameterType="org.fool.spring.model.Goods">
insert into goods
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="name != null">
name,
</if>
<if test="state != null">
state,
</if>
<if test="version != null">
version,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
<if test="state != null">
#{state,jdbcType=TINYINT},
</if>
<if test="version != null">
#{version,jdbcType=BIGINT},
</if>
<if test="createTime != null">
#{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="org.fool.spring.model.Goods">
update goods
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="state != null">
state = #{state,jdbcType=TINYINT},
</if>
<if test="version != null">
version = #{version,jdbcType=BIGINT},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=TIMESTAMP},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=TIMESTAMP},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="org.fool.spring.model.Goods">
update goods
set name = #{name,jdbcType=VARCHAR},
state = #{state,jdbcType=TINYINT},
version = #{version,jdbcType=BIGINT},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_time = #{updateTime,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
GoodsMapperExt.java
package org.fool.spring.dao.mapper.extension;
import org.fool.spring.model.Goods;
public interface GoodsMapperExt {
int casUpdateByPrimaryKey(Goods goods);
}
GoodsMapperExt.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.fool.spring.dao.mapper.extension.GoodsMapperExt">
<resultMap id="BaseResultMap" type="org.fool.spring.model.Goods">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="state" jdbcType="TINYINT" property="state"/>
<result column="version" jdbcType="BIGINT" property="version"/>
<result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
</resultMap>
<sql id="Base_Column_List">
id, name, state, version, create_time, update_time
</sql>
<update id="casUpdateByPrimaryKey" parameterType="org.fool.spring.model.Goods">
update goods
set name = #{name,jdbcType=VARCHAR},
state = #{state,jdbcType=TINYINT},
version = version + 1
where id = #{id,jdbcType=BIGINT} and version = #{version,jdbcType=BIGINT}
</update>
</mapper>
Application.java
package org.fool.spring; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
@MapperScan("org.fool.spring.dao.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationTest.java
package org.fool.spring.test; import org.fool.spring.Application;
import org.fool.spring.dao.mapper.GoodsMapper;
import org.fool.spring.dao.mapper.extension.GoodsMapperExt;
import org.fool.spring.model.Goods;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@EnableAutoConfiguration
public class ApplicationTest {
@Autowired
private GoodsMapper goodsMapper; @Autowired
private GoodsMapperExt goodsMapperExt; @Test
public void testSelect() {
Goods result = goodsMapper.selectByPrimaryKey(1L);
assert 1 == result.getId();
} @Test
public void testUpdate() {
Goods goods1 = goodsMapper.selectByPrimaryKey(1L);
Goods goods2 = goodsMapper.selectByPrimaryKey(1L); goods1.setName("iPhone");
goods2.setName("iPhoneXR"); int result1 = goodsMapperExt.casUpdateByPrimaryKey(goods1);
assert 1 == result1;
int result2 = goodsMapperExt.casUpdateByPrimaryKey(goods2);
assert 0 == result2;
}
}
MySQL使用版本号实现乐观锁的更多相关文章
- 谈谈mysql的悲观和乐观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一 ...
- MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解
文章出处:https://www.souyunku.com/2018/07/30/mysql/?utm_source=tuicool&utm_medium=referral MySQL/Inn ...
- 通过版本号实现乐观锁(MVCC)
乐观锁大多是基于数据版本记录的机制实现 , 如 , 为每一行数据增加一个整型版本标识(version) , 每次数据更新都把版本号+1 工作原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本 ...
- 使用mysql乐观锁解决并发问题
案例说明: 银行两操作员同时操作同一账户.比如A.B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户扣除50元,A先提交,B后提交.最后实际账户余额为1000 ...
- 【数据库】mysql深入理解乐观锁与悲观锁
转载:http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时 ...
- mysql悲观锁与乐观锁
简介 数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 用途 乐观锁和悲观锁是并发控制主要采用的技术手段.无论是悲观 ...
- MySQL学习(四)深入理解乐观锁与悲观锁
转载自:http://www.hollischuang.com/archives/934 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据 ...
- 面试官:小伙子,给我说一下mysql 乐观锁和悲观锁吧
悲观锁介绍 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中, 将数据处于锁定状态.悲观锁的实现,往往依靠数据库 ...
- [数据库锁机制] 深入理解乐观锁、悲观锁以及CAS乐观锁的实现机制原理分析
前言: 在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念.数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务 ...
随机推荐
- MySQL工作中遇到的问题记录
1:log_slave_updates: 从库1搭建级联从库2,从库1需要开启log_slave_updates,修改/etc/my.cnf,增加一行log_slave_updates=1,重启数据库 ...
- elasticsearch-7.2.0 在windows环境的部署应用
1.下载解压版"elasticsearch-7.2.0-windows-x86_64.zip",解压至"D:\server\elasticsearch-7.2.0&quo ...
- IIS网站绑定域名
你新建的网站右键-->编辑绑定-->添加 -->类型:http,IP地址:全部未分配,端口号:80,主机名:你的域名,例如yangche.cn-->确定
- 进程之间的通讯Queue简单应用
#进程间通讯--Queue #Process有时需要通信的,操作系统提供了很多机制来实现进程之间的通讯 #而Queue就是其中一个 #1.Queue的使用 #可以使用multiprocessing模块 ...
- (1)leetcode刷题Python笔记——两数之和
题目如下: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数 ...
- 关于Python的10大实用编程技巧
Python 是一种通用的脚本开发语言,比其他编程语言更加简单.易学,其面向对象特性甚至比Java.C#..NET更加彻底,因此非常适合快速开发. Python 已经成为最受欢迎的程序设计语言之一 ...
- JavaScript Is or isNot
读书笔记,简化代码--不对外公布,只是做笔记使用. var superman = { name: "Superman", strength: "Super", ...
- mybatis配置mapper.xml 的基本操作
XML 映射文件 本文参考mybatis中文官网进行学习总结:http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html MyBatis 的真正强大在于它的 ...
- Yii2 错误 'Headers already sent.'
错误日志如下: __source__: __topic__: web category: yii\web\HeadersAlreadySentException ip: level: message: ...
- JQuery的链式编程与隐式迭代
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...