3

我想使用@VersionJPA 和 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,都进行了更改,然后一个接一个地执行,在这种情况下,两个更改都将被应用,最后一个将“获胜”。我希望这种情况也能检测到并发修改(第二个用户应该得到一个异常)。

我怎样才能实现它?

4

1 回答 1

0

如果我正确理解了您的问题,您只需要为您的private int version字段设置一个设置器,当您更新实体时,您将其设置在您的实体中。当然,您的 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);
于 2013-09-10T16:12:25.677 回答