一尘不染

手动设置版本字段时的乐观锁定不会引发异常

hibernate

我有一个使用Spring Data JPA的Spring Boot 1.3.M1 Web应用程序。对于乐观锁定,我正在执行以下操作:

  1. 注释实体中的版本列:@Version private long version;。通过查看数据库表,我确认该字段正在正确递增。
  2. 当用户请求实体进行编辑时,也发送该version字段。
  3. 当用户在编辑后按下提交时,将version字段作为隐藏字段或其他内容接收。
  4. 服务器端,获取实体的新副本,然后与字段一起更新所需的version字段。像这样:
    User user = userRepository.findOne(id);
    

    user.setName(updatedUser.getName());
    user.setVersion(updatedUser.getVersion());
    userRepository.save(user);

我期望当版本不匹配时会抛出异常。但事实并非如此。谷歌搜索,我发现一些帖子说我们不能设置@Vesion附加实体的属性,就像我在上面的第三条语句中所做的那样。

因此,我猜测我将不得不手动检查版本不匹配并自己抛出异常。那是正确的方法,还是我错过了什么?


阅读 217

收藏
2020-06-20

共1个答案

一尘不染

不幸的是,(至少对于Hibernate而言)@Version手动更改字段不会使它成为另一个“版本”。即乐观并发检查是针对读取实体时获取的版本值而不是针对实体更新时的版本字段进行的。

例如

这会工作

Foo foo = fooRepo.findOne(id);  // assume version is 2 here
foo.setSomeField(....);

// Assume at this point of time someone else change the record in DB, 
// and incrementing version in DB to 3

fooRepo.flush();  // forcing an update, then Optimistic Concurrency exception will be thrown

但是这不起作用

Foo foo = fooRepo.findOne(id);  // assume version is 2 here
foo.setSomeField(....);
foo.setVersion(1);
fooRepo.flush();  // forcing an update, no optimistic concurrency exception
                  // Coz Hibernate is "smart" enough to use the original 2 for comparison

有一些解决方法。最简单的方法可能是自己实施乐观并发检查。我曾经有一个工具来进行“
DTO到模型”数据填充,并且在那里放置了版本检查逻辑。另一种方法是将逻辑放在setVersion()其中,而不是真正设置版本,而是执行版本检查:

class User {
    private int version = 0;
    //.....

    public void setVersion(int version) {
        if (this.version != version) {
            throw new YourOwnOptimisticConcurrencyException();
        }
    }

    //.....
}
2020-06-20