@Version
注释在 JPA 中是如何工作的?
我找到了各种答案,其摘录如下:
JPA 使用实体中的版本字段来检测对同一数据存储记录的并发修改。当 JPA 运行时检测到同时修改同一记录的尝试时,它会向最后尝试提交的事务抛出异常。
但我仍然不确定它是如何工作的。
同样来自以下几行:
您应该考虑版本字段不可变。更改字段值会产生未定义的结果。
这是否意味着我们应该将我们的版本字段声明为final
?
@Version
注释在 JPA 中是如何工作的?
我找到了各种答案,其摘录如下:
JPA 使用实体中的版本字段来检测对同一数据存储记录的并发修改。当 JPA 运行时检测到同时修改同一记录的尝试时,它会向最后尝试提交的事务抛出异常。
但我仍然不确定它是如何工作的。
同样来自以下几行:
您应该考虑版本字段不可变。更改字段值会产生未定义的结果。
这是否意味着我们应该将我们的版本字段声明为final
?
但我仍然不确定它是如何工作的?
假设一个实体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
.
这是否意味着我们应该将我们的版本字段声明为最终的
不,但你可以考虑让 setter 受到保护,因为你不应该调用它。
尽管@Pascal 的答案完全有效,但根据我的经验,我发现下面的代码有助于完成乐观锁定:
@Entity
public class MyEntity implements Serializable {
// ...
@Version
@Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
private long version = 0L;
// ...
}
为什么?因为:
@Version
null
optlock
而不是version
.如果应用程序仅使用JPA 将数据插入数据库,则第一点无关紧要,因为 JPA 供应商将在创建时强制0
执行@version
字段。但几乎总是使用普通的 SQL 语句(至少在单元和集成测试期间)。
每次在数据库中更新实体时,版本字段都会增加一。更新数据库中实体的每个操作都将附加WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
到其查询中。
在检查操作的受影响行时,jpa 框架可以确保在加载和持久化实体之间没有并发修改,因为当加载和持久化之间的版本号增加时,查询不会在数据库中找到您的实体。
用于确保一次仅更新一次的版本。JPA 提供者将检查版本,如果预期版本已经增加,那么其他人已经更新了实体,因此将引发异常。
所以更新实体值会更安全、更乐观。
如果值变化频繁,那么您可能会考虑不使用版本字段。例如“具有计数器字段的实体,每次访问网页时都会增加”
只需添加更多信息。
JPA 在后台为您管理版本,但是当您通过 更新记录时它不会这样做JPAUpdateClause
,在这种情况下,您需要手动将版本增量添加到查询中。
通过 JPQL 更新也可以这样说,即不是对实体的简单更改,而是对数据库的更新命令,即使这是由 hibernate 完成的
佩德罗