2011-07-14 48 views
5

在我的Web應用程序中,我有幾個線程可能同時訪問相同的數據,爲什麼我決定實現樂觀(版本控制)和悲觀鎖定Hibernate。如何正確鎖定和重新加載實體

目前我使用下面的模式來鎖定一個實體,並在其上執行寫操作(使用彈簧事務管理器和事務劃分@Transactional):

@Transactional 
public void doSomething(entity) { 
    session.lock(entity, LockMode.UPGRADE); 
    session.refresh(entity); 

    // I change the entity itself as well as entites in a relationship. 
    entity.setBar(...); 
    for(Child childEntity : entity.getChildren()) { 
     childEntity.setFoo(...); 
    } 
} 

但是,有時我得到StaleObjectException當@事務性衝突告訴我一個ChildEntity已經被同時修改,並且現在有一個錯誤的版本。

我想我是不正確刷新entity及其子女所以我正在處理陳舊的數據。有人可以指出如何實現這一點?我的一些想法包括清除持久性上下文(會話)或再次調用session.lock(entity, LockMode.READ),但我不確定這裏是什麼是正確的。

感謝您的幫助!

回答

0

爲什麼你讓「LockMode.UPGRADE」和樂觀鎖定在一起?看起來像有爭議的事情。

Hibernate從不鎖定內存中的對象,並始終使用數據庫的鎖定機制。另外,「如果數據庫不支持請求的鎖定模式,Hibernate會使用合適的備用模式而不是拋出異常,這確保了應用程序的可移植性。」這意味着,如果你的數據庫不支持SELECT ... FOR UPDATE,很可能你會得到這些異常。

另一個可能的原因是你沒有爲孩子使用「org.hibernate.annotations.CascadeType.LOCK」。

+0

我使用MySQL InnoDB,它支持「SELECT ... FOR UPDATE」,所以我在這裏沒有看到問題。此外,爲了在進行併發修改時獲得「通知」,我也利用樂觀鎖定。 – Erik

+0

對不起,錯過了你對孩子有問題的地方(用粗體:))。我已經更新了答案...... – Stas

+0

在我的代碼中,我總是鎖定實體,即使我只想更新實體而不是子代。所以也應該有獲得的鎖。我主要關心的是,我並不僅僅使用刷新工具來處理最新的實體/孩子。 – Erik

1

你可能想看看這個休眠問題:LockMode.Upgrade doesn't refresh entity values

簡而言之:如果給定的實體已經預加載,Hibernat不會在成功鎖定後執行選擇。收到鎖後,您需要爲您自己刷新實體。