springboot + mybatis-plus 事务并发更新同一行数据导致更新丢失问题保证并发的前提下的解决方法不适用乐观锁 附上完整代码
问题描述:
在使用 SpringBoot 和 Mybatis-Plus 进行数据库操作时,如果多个事务并发更新同一行数据,可能会导致更新丢失的问题,即最后一次更新的结果覆盖了之前的所有更新结果,造成数据不一致。
解决方法:
在不使用乐观锁的情况下,可以使用数据库的悲观锁机制来解决并发更新的问题。具体实现方式如下:
-
在 SQL 语句中使用 FOR UPDATE 关键字,对需要更新的行进行加锁。
-
在代码中使用 @Transactional 注解开启事务,并在需要更新数据的方法上添加 synchronized 关键字,保证同一时刻只有一个线程能够访问该方法。
-
在更新数据时,先查询出需要更新的数据,并判断数据是否已被其他事务更新过,如果已被更新,则回滚事务重新尝试更新,直到更新成功为止。
-
在更新数据时,需要手动提交事务,以释放悲观锁。
代码实现:
- SQL 语句中使用 FOR UPDATE 关键字:
UPDATE table_name SET column1 = value1 WHERE id = ? FOR UPDATE
- 代码中使用 @Transactional 注解和 synchronized 关键字:
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public synchronized void updateUser(User user) {
// 查询需要更新的数据,并加锁
User dbUser = userMapper.selectById(user.getId());
// 判断数据是否已被其他事务更新过
if (dbUser.getVersion() > user.getVersion()) {
throw new BusinessException("数据已被其他事务更新,请重试");
}
// 更新数据
user.setVersion(user.getVersion() + 1);
userMapper.updateById(user);
// 手动提交事务,释放悲观锁
TransactionAspectSupport.currentTransactionStatus().flush();
}
}
- 控制器中调用更新方法:
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PutMapping
public Result updateUser(@RequestBody User user) {
userService.updateUser(user);
return Result.success();
}
}
完整代码:
UserMapper.java
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
User.java
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private Integer age;
private Integer version;
}
UserService.java
public interface UserService {
void updateUser(User user);
}
UserServiceImpl.java
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public synchronized void updateUser(User user) {
// 查询需要更新的数据,并加锁
User dbUser = userMapper.selectById(user.getId());
// 判断数据是否已被其他事务更新过
if (dbUser.getVersion() > user.getVersion()) {
throw new BusinessException("数据已被其他事务更新,请重试");
}
// 更新数据
user.setVersion(user.getVersion() + 1);
userMapper.updateById(user);
// 手动提交事务,释放悲观锁
TransactionAspectSupport.currentTransactionStatus().flush();
}
}
UserController.java
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PutMapping
public Result updateUser(@RequestBody User user) {
userService.updateUser(user);
return Result.success();
}
}
``
原文地址: https://www.cveoy.top/t/topic/dbHS 著作权归作者所有。请勿转载和采集!