乐观锁(Optimistic Locking)是解决并发问题的重要机制。它通过在数据更新时验证数据版本来确保数据的一致性,从而避免并发冲突。与悲观锁不同,乐观锁并不依赖数据库的锁机制,而是通过检查数据的版本或标志字段来判断数据是否被其他事务修改过。
MyBatis-Plus 提供了便捷的乐观锁支持,通过在实体类中添加版本号字段(通常是一个 int 或 long 类型的字段),并在更新操作时检查版本号,以确保数据的一致性和完整性,同时不影响系统的并发性能。
乐观锁的工作原理
- 版本号字段:在实体类中添加一个版本号字段,通常为version
- 更新操作:在更新操作是,增加一个条件,检查版本号是否匹配。如果不匹配,表示数据已经被其他事务修改,更新操作失败。
执行SQL如下
update t_user balance=?,version=? where id=? and version=?
Mybatis-Plus通过拦截器会自定将乐观锁逻辑加入sql中
使用Mybatis-Plus注意:
乐观锁支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
仅支持updateById(id)
与update(entity,wrapper)
方法
另外,每次操作前都是先查询,替换,再更新,否则乐观锁无效
快速上手
建表
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL COMMENT '名字',
`balance` int(11) DEFAULT NULL COMMENT '余额',
`version` int(11) NOT NULL DEFAULT '0' COMMENT '乐观锁版本号',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
实体类
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Integer id;
private String name;
private Integer balance;
@Version
private Integer version;
private Date createTime;
private Date updateTime;
}
mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
service
@Service
public class UserService extends ServiceImpl<UserMapper, User> {
}
配置
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
// MybatisPlus 拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
测试代码
@SpringBootTest
public class MpApplicationTests {
@Autowired
private UserService userService;
@Test
public void test1(){
User user = userService.getById(1);
user.setName("xxx");
user.setBalance(18);
boolean b = userService.updateById(user);
System.out.println(b);
}
}
查看执行后的sql
UPDATE t_user SET name=?, balance=?, version=?, create_time=? WHERE id=? AND version=?
失效情况
// 这种情况由于没有先进行查询,乐观锁插件会失效
// 执行sql如下
// UPDATE t_user SET name=?, balance=? WHERE id=?
@Test
public void test1(){
User user = new User();
user.setId(1)
user.setName("xxx");
user.setBalance(18);
boolean b = userService.updateById(user);
System.out.println(b);
}