我想使用@Version
JPA 和 Hibernate 进行乐观并发控制。
我知道它在两个并行事务的典型场景中是如何工作的。我还知道,如果我有一个在表单和实体之间具有 1:1 映射的 CRUD,我可以将version
其作为隐藏字段传递并使用它来防止用户同时进行修改。
更有趣的情况如何,使用 DTO 或更改命令模式?是否也可以@Version
在这种情况下使用,以及如何使用?
让我给你举个例子。
@Entity
public class MyEntity {
@Id private int id;
@Version private int version;
private String someField;
private String someOtherField;
// ...
}
现在假设两个用户为此打开 GUI,进行一些修改并保存更改(不是同时,因此事务不会重叠)。
如果我传递整个实体,第二个事务将失败:
@Transactional
public void updateMyEntity(MyEntity newState) {
entityManager.merge(newState);
}
这很好,但我不喜欢到处传递实体的想法,有时会使用 DTO、更改命令等。
为简单起见,更改命令是一个映射,最终在某些服务上用于这样的调用:
@Transactional
public void updateMyEntity(int entityId, int version, Map<String, Object> changes) {
MyEntity instance = loadEntity(entityId);
for(String field : changes.keySey()) {
setWithReflection(instance, field, changes.get(field));
}
// version is unused - can I use it somehow?
}
显然,如果两个用户打开我的 GUI,都进行了更改,然后一个接一个地执行,在这种情况下,两个更改都将被应用,最后一个将“获胜”。我希望这种情况也能检测到并发修改(第二个用户应该得到一个异常)。
我怎样才能实现它?