丢失更新
多个人同时修改同一条记录,最后提交的把之前提交的数据全部覆盖。
解决方案
- 悲观锁(不推荐):串行化操作,不能同时操作,效率低。
- 乐观锁(推荐)
乐观锁
主要适用场景 :当要更新一条记录的时候,希望这条记录没有被别人更新,也就是说实现线程安全的数据更新。
乐观锁实现方式 :
- 取出记录,获取当前version字段;
- 更新时,带上这个version;
- 执行更新时,set version = newVersion where version = oldVersion;
- 如果version不对,就更新失败。
具体步骤
- 在数据库的相应表中添加
version
字段; - 在POJO类中添加
version
字段,并添加@Version
注解和@TableField(fill = FieldFill.INSERT)
; - 在实现元对象处理器接口的类中设置在添加操作时
version
字段自动填充的值; - 在配置类中注册
OptimisticLockerInterceptor
的Bean
。
具体实现
在MyBatis-Plus入门案例基础上进行修改。
User.java
package com.example.mybatis_plus_demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data
public class User {
//@TableId(type = IdType.ID_WORKER) mp自带策略,生成19位值,数字类型使用这种策略,比如long
//@TableId(type = IdType.ID_WORKER_STR) mp自带策略,生成19位值,字符串类型使用这种策略
//@TableId(type = IdType.INPUT) 设置id值
//@TableId(type = IdType.UUID) 随机唯一值
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
//添加时填充值
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//添加或修改时填充值
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
}
MybatisPlusConfig.java
package com.example.mybatis_plus_demo.config;
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;
@MapperScan("com.example.mybatis_plus_demo.mapper")
@Configuration
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
MyMetaObjectHandler.java
package com.example.mybatis_plus_demo.handler;
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 {
//使用mybatis-plus实现添加操作,这个方法执行
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
this.setFieldValByName("version",1,metaObject);
}
//使用mybatis-plus实现修改操作,这个方法执行
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
MybatisPlusDemoApplicationTests.java
package com.example.mybatis_plus_demo;
import com.example.mybatis_plus_demo.entity.User;
import com.example.mybatis_plus_demo.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MybatisPlusDemoApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void findAll() {
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
@Test
void addUser() {
User user = new User();
user.setName("faker");
user.setAge(30);
user.setEmail("lucy@qq.com");
int insert = userMapper.insert(user);
System.out.println("insert:" + insert);
}
@Test
void updateUser() {
User user = new User();
user.setId(1371994629343391747L);
user.setAge(120);
int i = userMapper.updateById(user);
System.out.println("update: "+i);
}
// 测试乐观锁
@Test
void testOptimisticLocker() {
User user = userMapper.selectById(1371994629343391748L);
user.setAge(200);
userMapper.updateById(user);
}
}
先进行数据的添加操作,后进行数据的查询操作,最后进行数据的更新操作,最后可以看到version字段变为2: