2011-01-06 44 views
0

我在Glassfish容器中使用JPA。我有以下Modell(未完成)用集合更新實體的JPA最佳實踐

@Entity 
public class Node { 
    @Id 
    private String serial; 
    @Version 
    @Column(updatable=false) 
    protected Integer version; 
    private String name; 
    @ManyToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE}) 
    private Set<LUN> luns = new HashSet<LUN>(); 

@Entity 
public class LUN { 
    @Id 
    private String wwid; 
    @Version 
    @Column(updatable=false) 
    protected Integer version; 
    private String vendor; 
    private String model; 
    private Long capacity; 
    @ManyToMany(mappedBy = "luns") 
    private Set<Node> nodes = new HashSet<Node>(); 

此信息每天都會更新。現在我的問題是,做到這一點的最佳做法是什麼。我的第一種方法是,我每天都在客戶端(帶有LUN)上生成節點對象,然後通過服務將它合併到數據庫(我想讓JPA完成這項工作)。

現在我做了一些沒有LUN的測試。我有一個無狀態的EJB以下服務:

public void updateNode(Node node) { 
    if (!nodeInDB(node)) { 
     LOGGER.log(Level.INFO, "persisting node {0} the first time", node.toString()); 
     em.persist(node); 
    } else { 
     LOGGER.log(Level.INFO, "merging node {0}", node.toString()); 
     node = em.merge(node); 
    } 
} 

測試:

@Test 
public void addTest() throws Exception { 
    Node node = new Node(); 
    node.setName("hostname"); 
    node.setSerial("serial"); 
    nodeManager.updateNode(node); 
    nodeManager.updateNode(node); 
    node.setName("newhostname"); 
    nodeManager.updateNode(node); 
} 

此作品,未經@Version場。通過@Version字段,我得到一個OptimisticLockException。

這是錯誤的方法?我是否必須始終執行em.find(...),然後通過getter和setter修改受管實體?

任何幫助表示讚賞。

BR Rene

回答

1

@version註釋用於啓用樂觀鎖定。

當您使用樂觀鎖定時,每次成功寫入表格都會增加一個版本計數器,每次持久化實體時都會讀取和比較版本計數器。如果在寫入時首次發現實體時讀取的版本與表中的版本不匹配,則會引發異常。

您的程序只在讀取一次版本列後多次更新表格。因此,在第二次調用persist()或merge()時,版本號不匹配,並且查詢失敗。這是使用樂觀鎖定時的預期行爲:您嘗試覆蓋自您第一次讀取該行以來發生更改的行。

要回答您的最後一個問題:您需要在每次寫入數據庫後讀取已更改的@version信息。你可以通過調用em.refresh()來做到這一點。

但是,您應該考慮重新考慮您的策略:樂觀鎖最適用於事務處理,以確保數據在用戶執​​行更改時的一致性。這些通常讀取數據,將其顯示給用戶,等待更改,然後在用戶完成任務後保留數據。在這種情況下,您不會真的需要也不需要多次寫入相同的數據行,因爲事務可能會因爲每次寫入調用都進行樂觀鎖定而失敗 - 這會使事情變得複雜而不是簡化。

+0

我不明白。我沒有添加任何內容。 – reen 2011-01-07 08:38:58