簡體   English   中英

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

[英]Hibernate/JPA version concurrency control and DTO/change command patterns

我想使用@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,更改命令等。

為簡單起見,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,都會進行更改,並一個接一個地執行,在這種情況下,將應用兩個更改,最后一個將“贏”。 我希望這個場景也檢測並發修改(第二個用戶應該得到一個例外)。

我怎樣才能實現它?

如果我正確理解您的問題,您只需要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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM