我想使用@Version
进行JPA& amp;的乐观并发控制。冬眠。
我知道它在两个并行事务的典型场景中是如何工作的。我也知道如果我在表单和实体之间有一个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,更改命令等。
为简单起见,change命令是一个映射,最终在某些服务上使用这样的调用:
@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,都会进行更改,并一个接一个地执行,在这种情况下,将应用两个更改,最后一个将“赢”。我希望这个场景也检测并发修改(第二个用户应该得到一个例外)。
我怎样才能实现它?
答案 0 :(得分:0)
如果我正确理解您的问题,您只需要为private int version
字段设置一个setter,当您更新实体时,就可以在实体中设置它。当然,您的DTO必须始终传输版本数据。最后,您还可以这样做:
MyEntity instance = loadEntity(entityId);
entityManager.detach(instance);
for(String field : changes.keySey()) {
setWithReflection(instance, field, changes.get(field));
}
//set also the version field, if the loop above does not set it
entityManager.merge(instance);