乐观锁认为绝大部分情况下,不会同时修改数据,所以乐观锁不上锁,只是在更新的时候检查一下,在此期间是否修改了数据。要实现乐观锁我们需要给表新增一个 列,每次更新 都加 1,实现代码如下:

1.数据表和实体类新增 字段

// User.java
public class User {
    private Long id;
    private String name;
    private String email;
    private Long version; // 版本号
    // 省略其他属性和方法...
}

2.业务层实现

// UserServiceImpl.java
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public ResponseEntity getUserById(Long userId) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            return new ResponseEntity(HttpStatus.NOT_FOUND);
        }
        // 生成 ETag
        String etag = ""user-" + user.getVersion() + """;
        HttpHeaders headers = new HttpHeaders();
        headers.setETag(etag);
        return new ResponseEntity(user, headers, HttpStatus.OK);
    }
    @Override
    @Transactional
    public void updateUser(Long userId, User updatedUser, String ifMatch) {
        // 获取当前数据库中的用户信息
        User currentUser = userMapper.selectById(userId);
        // 如果没有提供 If-Match,则允许更新
        if (ifMatch == null) {
            // 版本号递增
            updatedUser.setVersion(updatedUser.getVersion() + 1);
            // 更新用户信息
            userMapper.updateById(updatedUser);
            return;
        }
        // 校验 If-Match 请求头
        String currentEtag = ""user-" + currentUser.getVersion() + """;
        if (!currentEtag.equals(ifMatch)) {
            throw new BusinessException("冲突:资源已被另一个请求修改");
        }
        // 版本号递增
        updatedUser.setVersion(updatedUser.getVersion() + 1);
        // 更新用户信息
        userMapper.updateById(updatedUser);
    }
}

假如有两个事务并发的事务 A 和事务 B,加上乐观锁后,如果事务 A 正在更新一行数据,而事务 B 在相同数据上进行读操作,事务 B 会选择等待,直到事务 A 完成。这是因为在 Read 隔离级别下,读取的事务会持有读锁,允许其他事务并行读取,但会阻止写入。

如果事务 A 正在更新一行数据,而事务 B 也尝试更新相同数据,事务 B 就会抛出异常(例如 tion)而不是等待。这是因为在 Read 隔离级别下,写入操作会持有写锁,不允许其他事务并行写入相同的数据。

3. 实现

// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/{userId}")
    public ResponseEntity getUser(@PathVariable Long userId) {
        return userService.getUserById(userId);
    }
    @PutMapping("/{userId}")
    public ResponseEntity updateUser(
            @PathVariable Long userId,
            @RequestBody User updatedUser,
            @RequestHeader(value = HttpHeaders.IF_MATCH, required = false) String ifMatch) {
        try {
            userService.updateUser(userId, updatedUser, ifMatch);
            return new ResponseEntity("更新用户成功", HttpStatus.OK);
        } catch (BusinessException e) {
            return new ResponseEntity(e.getMessage(), HttpStatus.CONFLICT);
        }
    }
}

希望你觉得这篇文章有用。如果有任何问题或意见,请随时留言,感谢阅读!

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,永久会员只需109元,全站资源免费下载 点击查看详情
站 长 微 信: nanadh666

声明:1、本内容转载于网络,版权归原作者所有!2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!