Mybatis-plus实现乐观锁
教程目录:
教程一: Mybatis-plus使用教程
教程二:mybatis-plus的字段自动填充
教程三:Mybatis实现物理删除和逻辑删除
教程四:Mybatis-plus实现乐观锁
教程五:Mybatis-plus实现条件查询
扫码关注公众号,更多资料尽在掌握。
1.概念。
首先要明白什么是乐观锁,什么是悲观锁?
悲观锁:当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这就悲观锁,在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,还会降低并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据。
乐观锁:乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。
说的通俗易懂点,悲观锁很悲观,每次去操作数据时,都会认为别人会去占有它,所以每次都会加锁,防止别人占用;乐观锁;每次去操作数据时,都会认为这里不会有人来跟我抢,就不加锁,只会在提交数据的时候会检测数据冲突。
在这里我们先看一下一个小问题,
乐观锁,就是在提交事务的时候判断一下数据是否改变,如果没有改变,那么可以提交成功。这里就会出现一个问题了,请看图:
分析:这就是典型的ABA问题,比如说一个线程1从数据库中取出库存数3,这时候另一个线程2也从数据库中库存数3,并且线程2进行了一些操作变成了2,然后线程2又将库存数变成3,这时候线程1进行提交操作发现数据库中仍然是3,然后线程1操作成功。尽管线程1的提交操作成功,但是不代表这个过程就是没有问题的。
改进方法,在数据表中添加一个version字段,这个代表数据版本,只要每次修改了数据,version的值就会改变,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。
这样就可以解决ABA问题
接下来我们来实现 Mybatis-plus乐观锁。
实现原理: 取出记录时,获取当前的version ,更新的时候带上这个version,事务提交的时候,比较version,version一致,更新成功,不一致,失败。
第一步、在数据表中添加一个version字段,代表数据版本号。
第二步、在实体类中添加一个version字段。并加上 @Version和
@TableField(fill = FieldFill.INSERT)注解。 这里要实现乐观锁仅需要加上一个 @Version注解即可,我们加上这个@TableField(fill = FieldFill.INSERT)注解,是给它一个初始值。
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
//create_time
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@TableLogic
private Integer deleted;
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
}
第三步、在配置类中配置乐观锁的插件。
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
public class MybatisConfig {
//逻辑删除
@Bean
public ISqlInjector iSqlInjector(){
return new LogicSqlInjector();
}
//乐观锁
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
第四步、实现version字段的自动填充,给version一个初始值1。
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//当mp实现添加操作的时候,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
this.setFieldValByName("version",1,metaObject);
}
//当mp实现修改操作的时候,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
到这里乐观锁就实现成功了,打开测试类测试。
首先执行一个添加方法。
成功之后查看数据表,version字段成功插入。
我们再看看修改这条数据,version会不会改变。注意这里修改的时候,要先把他查询出来,这样才会生效。
查看数据表,version变为2,成功。