2012-11-02 49 views
3

我想用@Version进行乐观并发控制,使用JPA & Hibernate。Hibernate/JPA版本并发控制和DTO /更改命令模式

我知道它是如何工作在两个并行交易的典型场景。我也知道,如果我在表单和实体之间有1:1映射的CRUD,我可以通过version作为隐藏字段,并用它来防止用户同时修改。

什么更有趣的情况下,它使用的DTO或更改命令模式?在这种情况下是否可以使用@Version以及如何?

让我给你举个例子。现在,让我们说两个用户为此打开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两种,做出改变,并且一个接一个地执行,在这种情况下,两个改变都将被应用,最后一个将会“获胜”。我想这个场景也检测到并发修改(第二个用户应该会得到一个异常)。

我该如何实现它?

回答

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