学习要求

良好的java基础, 熟悉SpringBoot框架,熟悉Mybatis框架

教学目标

了解并掌握MyBatis-Plus 乐观锁实现


概念

多线程环境下如何保证数据库操作安全呢?常用的一种解决方案就是对操作数据表进行加锁处理。根据实现思路不同分:悲观锁与乐观锁2种。

悲观锁:悲观的认为多事务操作同一数据是及其不安全的,所以A事务在操作数据时,其他任何事务不允许对该数据进行修改,只能等待A事务操作结束后才可以执行。

乐观锁:乐观的认为A事务在操作数据时,期间不会有其他事务进行干扰,能顺利完成事务操作。

实现

悲观锁

悲观锁解决方案非常简单,直接在操作sql中加上for update 语句即可

update 表  set  列=值 where 条件列=值   for update
select * from 表 where 条件列=值   for update

使用for update 操作,可以认为是给每次操作都加上表级别悲观锁,在事务没结束前,其他事务必须等待。

事务1

步骤1:启动事务

步骤2:使用悲观锁方式查询部门表(此时锁表)

步骤3:提交事务 

mybatis cursor mysql 锁级别_悲观锁

事务2

在事务执行到步骤2时,执行下面逻辑,发现事务停滞,等待事务1commit之后才继续往下执行。

mybatis cursor mysql 锁级别_乐观锁_02

注意:并发环境下都不建议使用悲观锁,因为悲观锁容易锁表,导致事务等待,性能低下。

乐观锁

乐观锁操作跟悲观锁区别在于不对操作的数据表进行加锁,而是使用version字段去规避。

以部门表为例子

步骤1:给部门表添加version列,默认值为0

mybatis cursor mysql 锁级别_spring boot_03

步骤2:实现乐观锁操作

需求: 修改id=1数据sn字段改为:kfb

1>先查询

select id, name, sn, version from department where id = 1;

得到该数据列version = 0

2>再修改, 注意version版本更新

update department set sn = "kfb", version = version + 1 where id = 1 and version = 0

这里有2个注意要点:

1:更新 sn字段的同时,version列数据也跟随变动

2:更新sql 除了基本的id=1条件外,多了version = 0 条件。

为什么要这么设计? 原因:乐观锁操作没有加锁,任意事务都可以同步操作,多事务同时操作,总有一个先成功,一成功则修改了version字段值,其他事务version 条件自然就不上啦,update失败。

mybatis cursor mysql 锁级别_乐观锁_04

步骤3:判断更新是否成功

乐观锁判断是否更新成功,就看执行update语句之后,返回的影响数据行数,如果行数大于0表示修改成功,如果行数等于0表示修改失败,放弃这次修改,一切重来。直到修改成功为止。

MyBatis-Plus版乐观锁

MyBatis-Plus 使用@Version注解实现乐观锁

还是以部门表为例

步骤1:在部门表添加version字段

mybatis cursor mysql 锁级别_悲观锁_05

 步骤2:部门实体对象添加version字段

public class Department {
    //省略其他字段
    @Version
    private int version;
 
}

步骤3:配置类中配置支持拦截器乐观锁

@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

 步骤4:正常执行更新方法

@Test
public void testUpdate(){
    //先查询
    Department dept = departmentMapper.selectById(1L);
    //再替换
    dept.setId(1L);
    dept.setName("小卖部");
    dept.setSn("sell");
    //最后更新
    departmentMapper.updateById(dept);
}

 执行后SQL

UPDATE department SET name=?, sn=?, version=? WHERE id=? AND version=?

MyBatis-Plus会自动讲乐观锁逻辑加载到sql中

使用Mybatis-Plus注意:

乐观锁支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime 仅支持 updateById(id)update(entity, wrapper) 方法 另外,每次操作前都是先查询,替换,再更新,否则乐观锁无效