乐观锁与悲观锁
乐观锁
认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只在更新数据的时候判断之前有没有别的线程更新了这个数据。如果有被别的线程更新,则再次重试更新。
悲观锁
认为自己在使用数据时一定有别的线程来修改数据,所以在获取数据的时候会先加锁,确保数据不会被别的线程修改。
MybatisPlus实现乐观锁
实现原理
- 查询记录时,获取当前记录的version(版本号)
- 更新步骤1查询出的记录时,使用version,作为更新条件,并且执行更新时,要给version字段赋新值
- 如果version不对,就更新失败
代码原理如下:
# 1、查询记录
select id,name,age,sex,version from person where id = 001;
# 假设查出来的记录是:001,王二,30,男,1
# 2、更新时,使用version作为更新条件,并且给version赋新值
update person
set name = '张三',
version = 2
where id = 001
and version = 1;
# 3. 此时,在做更新时,如果version的值,已经被别的线程抢先更新了,不再等于1,那么步骤2就会执行失败;要实现更新,重试即可。
实现步骤
- 新增数据库表字段,语句如下:
ALTER TABLE mybatis_plus_user ADD COLUMN `version` INT(32) DEFAULT 1 COMMENT '乐观锁';
- 新增对应实体类字段
/**
* 乐观锁版本号
*/
@Version
private Integer version;
- 注册组件
package springboot.mybatisplus.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @Description MybatisPlus配置类
* @ClassName MybatisPlusConfig
* @Author yuhuofei
* @Date 2022/4/3 15:20
* @Version 1.0
*/
@Configuration
@EnableTransactionManagement
@MapperScan("springboot.mybatisplus.mapper")
public class MybatisPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
测试
- 在测试类中,新增一个测试方法
@Test
public void testOptimisticLocker() {
//查询一条记录
MybatisPlusUser mybatisPlusUser = mybatisPlusUserMapper.selectById(1508489260037705729L);
//更新
mybatisPlusUser.setName("张三");
mybatisPlusUser.setAge(30);
mybatisPlusUserMapper.updateById(mybatisPlusUser);
}
- 执行,具体执行过程如下
JDBC Connection [HikariProxyConnection@344080246 wrapping com.mysql.cj.jdbc.ConnectionImpl@6917bb4] will not be managed by Spring
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM mybatis_plus_user WHERE id=?
==> Parameters: 1508489260037705729(Long)
<== Columns: id, name, age, email, create_time, update_time, version
<== Row: 1508489260037705729, hahaha, 28, 1ddgds97@qq.com, 2022-03-29 01:00:41, 2022-03-29 01:01:41, 1
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@23706db8]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48528634] was not registered for synchronization because synchronization is not active
2022-04-03 15:35:37.675 INFO 14740 --- [ main] s.m.handle.MyMetaObjectHandler : start update fill ....
JDBC Connection [HikariProxyConnection@243596165 wrapping com.mysql.cj.jdbc.ConnectionImpl@6917bb4] will not be managed by Spring
==> Preparing: UPDATE mybatis_plus_user SET name=?, age=?, email=?, create_time=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: 张三(String), 30(Integer), 1ddgds97@qq.com(String), 2022-03-29 01:00:41.0(Timestamp), 2022-03-29 01:01:41.0(Timestamp), 2(Integer), 1508489260037705729(Long), 1(Integer)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48528634]
- 结果
- 注意点
如果当前更新失败,说明version字段在查询之后,被其它线程抢先更新了;此时如果依旧想继续更新,那么重新执行一遍方法即可。