Answers:
但是我仍然不确定它是如何工作的?
假设一个实体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
不可以,但是您可以考虑使二传手受到保护,因为您不应该称呼它。
long
或对其进行调用longValue()
。您需要显式null
检查。我自己不会明确初始化它,因为该字段应该由ORM提供程序管理。我认为它是0L
在第一次插入数据库时设置的,因此如果将其设置null
为此,则表明该记录尚未持久。
Long
而不是基元,因为它可能会将空版本字段视为该实体是新实体的指示符。如果实体是新实体(版本为null指示),Spring将调用。但是,如果version具有值,则它将调用。这就是为什么声明为包装类型的版本字段应保持未初始化状态的原因。long
@Version
SimpleJpaRepository.save(entity)
em.persist(entity)
em.merge(entity)
尽管@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语句(至少在单元和集成测试期间)。
u_lmod
(用户最后修改),其中用户可以是人工的,也可以是定义的(自动化的)流程/实体/应用程序(因此,另外u_lmod_id
进行存储也很有意义)。(我认为这是非常基本的业务元属性)。通常将其值存储为UTC中的DATE(TIME)(表示有关年...毫秒的信息)(如果编辑器的时区“位置”对于存储时区很重要,则使用时区)。另外对于DWH同步方案非常有用。
每次在数据库中更新实体时,版本字段都会增加一个。更新数据库中实体的每个操作都将附加WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
到其查询中。
在检查操作的受影响行时,jpa框架可以确保在加载和持久存储实体之间没有并发修改,因为当在加载和持久存储之间版本号增加时,查询不会在数据库中找到您的实体。
只需添加更多信息。
JPA可以为您管理版本,但是当您通过来更新记录时,JPA不会这样做JPAUpdateClause
,在这种情况下,您需要手动将版本增量添加到查询中。
对于通过JPQL进行更新,也可以说相同,即,不是对实体的简单更改,而是对数据库的更新命令,即使该命令是由休眠完成的也是如此
佩德罗
JPAUpdateClause
啊
UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]
。如果有人更新了该记录,old.version
它将不再与数据库中的记录匹配,并且where子句将阻止更新发生。“行更新”将为0
,JPA可以检测到该行以得出发生并发修改的结论。