@Version注释在 JPA 中是如何工作的?
@Version
我找到了各种各样的答案,摘录如下:
JPA 使用实体中的 version 字段来检测对同一数据存储记录的并发修改。当 JPA 运行时检测到并发修改相同记录的尝试时,它会向尝试最后提交的事务抛出一个异常。
但我仍然不知道它是如何工作的。
同样来自以下几行:
您应该考虑版本字段是不可变的。更改字段值会产生未定义的结果。
这是否意味着我们应该将版本字段声明为 final?
final
每次更新数据库中的实体时,版本字段将增加一个。更新数据库中实体的每个操作都将向其查询追加 WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE。
WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
在检查受影响的操作行时,jpa 框架可以确保在加载和持久化实体之间没有并发修改,因为当数据库的版本号在加载和持久化之间增加时,查询不会在数据库中找到实体。
但我还是不知道它是如何工作的?
假设一个实体 MyEntity有一个带注释的 version属性:
MyEntity
version
@Entity public class MyEntity implements Serializable { @Id @GeneratedValue private Long id; private String name; @Version private Long version; //... }
在更新时,用 @Version注释的字段将被增加并添加到 WHERE子句中,类似于下面这样:
WHERE
UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
如果 WHERE子句无法匹配记录(因为同一实体已被另一个线程更新) ,那么持久性提供程序将抛出 OptimisticLockException。
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; // ... }
为什么? 因为:
null
optlock
如果应用程序使用 只有 JPA 将数据插入到数据库中,第一点并不重要,因为 JPA 供应商将在创建时对 @version字段强制执行 0。但是几乎总是使用纯 SQL 语句(至少在单元测试和集成测试期间)。
@version
0
只是多加了一点信息。
JPA 在底层为您管理版本,但是当您通过 JPAUpdateClause更新记录时,它不会这样做,在这种情况下,您需要手动向查询添加版本增量。
JPAUpdateClause
通过 JPQL 进行更新也可以这样说,即不是对实体进行简单的更改,而是对数据库进行更新命令,即使这是通过 hibernate 完成的
佩德罗