Hibernate / JPA版本的并发控制和DTO /更改命令模式

时间:2012-11-02 11:54:40

标签: java hibernate jpa concurrency optimistic-concurrency

我想使用@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,都会进行更改,并一个接一个地执行,在这种情况下,将应用两个更改,最后一个将“赢”。我希望这个场景也检测并发修改(第二个用户应该得到一个例外)。

我怎样才能实现它?

1 个答案:

答案 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);