Java-JPA -@Version 注释

@Version注释在 JPA 中是如何工作的?

我找到了各种各样的答案,摘录如下:

JPA 使用实体中的 version 字段来检测对同一数据存储记录的并发修改。当 JPA 运行时检测到并发修改相同记录的尝试时,它会向尝试最后提交的事务抛出一个异常。

但我仍然不知道它是如何工作的。


同样来自以下几行:

您应该考虑版本字段是不可变的。更改字段值会产生未定义的结果。

这是否意味着我们应该将版本字段声明为 final

158861 次浏览

每次更新数据库中的实体时,版本字段将增加一个。更新数据库中实体的每个操作都将向其查询追加 WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE

在检查受影响的操作行时,jpa 框架可以确保在加载和持久化实体之间没有并发修改,因为当数据库的版本号在加载和持久化之间增加时,查询不会在数据库中找到实体。

但我还是不知道它是如何工作的?

假设一个实体 MyEntity有一个带注释的 version属性:

@Entity
public class MyEntity implements Serializable {


@Id
@GeneratedValue
private Long id;


private String name;


@Version
private Long version;


//...
}

在更新时,用 @Version注释的字段将被增加并添加到 WHERE子句中,类似于下面这样:

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))

如果 WHERE子句无法匹配记录(因为同一实体已被另一个线程更新) ,那么持久性提供程序将抛出 OptimisticLockException

这是否意味着我们应该将 version 字段声明为 final

没有,但是你可以考虑让二传手受到保护,因为你不应该这样称呼它。

版本用于确保一次只进行一次更新。JPA 提供程序将检查版本,如果预期的版本已经增加,那么其他人已经更新了实体,因此将抛出异常。

因此,更新实体价值将更加安全,更加乐观。

如果值变化频繁,那么可以考虑不使用版本字段。例如“一个有计数器字段的实体,每次访问网页都会增加”

虽然@Pascal 的回答是完全有效的,但根据我的经验,我发现下面的代码有助于实现乐观锁定:

@Entity
public class MyEntity implements Serializable {
// ...


@Version
@Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
private long version = 0L;


// ...
}

为什么? 因为:

  1. 如果用 @Version注释的字段被意外地设置为 null,则乐观锁定 没用的
  2. 由于这个特殊字段不一定是对象的业务版本,为了避免误导,我更喜欢将这样的字段命名为 optlock而不是 version

如果应用程序使用 只有 JPA 将数据插入到数据库中,第一点并不重要,因为 JPA 供应商将在创建时对 @version字段强制执行 0。但是几乎总是使用纯 SQL 语句(至少在单元测试和集成测试期间)。

只是多加了一点信息。

JPA 在底层为您管理版本,但是当您通过 JPAUpdateClause更新记录时,它不会这样做,在这种情况下,您需要手动向查询添加版本增量。

通过 JPQL 进行更新也可以这样说,即不是对实体进行简单的更改,而是对数据库进行更新命令,即使这是通过 hibernate 完成的

佩德罗