主要适用场景
意图:
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
-
取出记录时,获取当前version
-
-
执行更新时, set version = newVersion where version = oldVersion
-
如果version不对,就更新失败
乐观锁配置需要2步 记得两步
1.插件配置
spring xml: <bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/> spring boot: @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }
2.注解实体字段 @Version
必须要!
特别说明:
-
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
-
整数类型下
newVersion = oldVersion + 1
-
newVersion
会回写到entity
中 -
仅支持
updateById(id)
与update(entity, wrapper)
方法 -
在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
示例
示例Java代码(参考代码)
int id = 100; int version = 2; User u = new User(); u.setId(id); u.setVersion(version); u.setXXX(xxx); if(userService.updateById(u)){ System.out.println("Update successfully"); }else{ System.out.println("Update failed due to modified by others"); }
示例SQL原理
update tbl_user set name = 'update',version = 3 where id = 100 and version = 2
-
pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/p6spy/p6spy --> <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.49</version> <scope>test</scope> </dependency> <!-- for testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
配置类
@Configuration public class MybatisPlusOptLockerConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
-
实体类
@Data public class User { @TableId(value = "id", type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; @Version private Integer version; }
-
Mapper
public interface UserMapper extends BaseMapper<User> { }
-
启动类
@SpringBootApplication @MapperScan("com.mp.locker.mapper") //不加在容器里面获取不了 public class LockerApplication { public static void main(String[] args) { SpringApplication.run(LockerApplication.class, args); } }
-
application.yml
spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:h2:tcp://192.168.180.115:19200/~/mem/test username: root password: test
-
测试类
@SpringBootTest class LockerApplicationTests { @Autowired(required = false) UserMapper userMapper; @Test public void testUpdateByIdSucc() { User user = new User(); user.setAge(18); user.setEmail("test@baomidou.com"); user.setName("optlocker"); user.setVersion(1); userMapper.insert(user); Long id = user.getId(); //INSERT INTO user ( name, version, email, age ) VALUES ( 'optlocker', 1, 'test@baomidou.com', 18 ) User userUpdate = new User(); userUpdate.setId(id); userUpdate.setAge(19); userUpdate.setVersion(1); //UPDATE user SET version=2, age=19 WHERE id=6 AND version=1 Assert.assertEquals("Should update success", 1, userMapper.updateById(userUpdate)); //6 optlocker 19 test@baomidou.com 2 值变成2 Assert.assertEquals("Should version = version+1", 2, userUpdate.getVersion().intValue()); } @Test public void testUpdateByIdFail() { User user = new User(); user.setAge(18); user.setEmail("test@baomidou.com"); user.setName("optlocker"); user.setVersion(1); //INSERT INTO user ( name, version, email, age ) VALUES ( 'optlocker', 1, 'test@baomidou.com', 18 ) userMapper.insert(user); Long id = user.getId(); User userUpdate = new User(); userUpdate.setId(id); userUpdate.setAge(19); userUpdate.setVersion(0); //UPDATE user SET version=1, age=19 WHERE id=7 AND version=0 userMapper.updateById(userUpdate); } @Test public void testUpdateByIdSuccWithNoVersion() { User user = new User(); user.setAge(18); user.setEmail("test@baomidou.com"); user.setName("optlocker"); user.setVersion(1); userMapper.insert(user); Long id = user.getId(); User userUpdate = new User(); userUpdate.setId(id); userUpdate.setAge(19); userUpdate.setVersion(null); Assert.assertEquals("Should update success as no version passed in", 1, userMapper.updateById(userUpdate)); User updated = userMapper.selectById(id); Assert.assertEquals("Version not changed", 1, updated.getVersion().intValue()); Assert.assertEquals("Age updated", 19, updated.getAge().intValue()); } /** * 批量更新带乐观锁 * <p> * update(et,ew) et:必须带上version的值才会触发乐观锁 */ @Test public void testUpdateByEntitySucc() { QueryWrapper<User> ew = new QueryWrapper<>(); ew.eq("version", 1); int count = userMapper.selectCount(ew); User entity = new User(); entity.setAge(28); entity.setVersion(1); Assert.assertEquals("updated records should be same", count, userMapper.update(entity, null)); ew = new QueryWrapper<>(); ew.eq("version", 1); Assert.assertEquals("No records found with version=1", 0, userMapper.selectCount(ew).intValue()); ew = new QueryWrapper<>(); ew.eq("version", 2); Assert.assertEquals("All records with version=1 should be updated to version=2", count, userMapper.selectCount(ew).intValue()); } }