2014-08-27 170 views
7

控制器邏輯:如何解決StaleObjectStateException使用JPA和Hibernate

def updateObject() { 

    Object o = Object.get(params.id as Long) 

    o.otherObjects.clear() 

    objectDataService.saveObject(o.id) 

    OtherObject newObject = new OtherObject; 

    o.addToOtherObjects(newObject) 

    objectDataService.saveObject(o.id) 

} 

ServiceLogic

def saveObject(long profileId) { 
    o.save(flush:true) 
} 

什麼的情況下,這只是工作的90%發生

問題

ERROR errors.GrailsExceptionResolver - StaleObjectStateException occurred when processing request: [GET] /controller/updateObject - parameters: 
stuff[]: data 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1]. 
Stacktrace follows: 
Message: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.path.Object#1] 

我已經通過相關的問題閱讀,發現merge叫你在上面看到。它解決了大約50%的案件,但不是全部。

+0

如果你將這個邏輯轉移到一個服務方法中,那麼你會不會更好? – 2014-09-03 15:29:46

+0

我不想超載該服務。你認爲這有意義嗎? – 2014-09-03 15:36:44

+0

將業務邏輯轉移到服務並使您的控制器儘可能瘦(通過瘦我的意思是用更少的代碼行)總是更好。這將有助於您在任何需要的地方重複使用代碼,並在測試中節省大量時間。 – 2014-09-03 19:41:57

回答

2

對於我們幾個不同的方法終於從經常存在的解決StaleObjectException異常:

object = object.refresh() 

檢索它們解決了我們大部分的StaleObjectExceptions後刷新對象。特別是在有人有可能從別處工作過同一個對象並改變了其中一些成員(大部分問題都是由收集成員提供)的情況下。

總體項目的穩定性:

wrongly linked resources 

我們對我們並不真正需要一個特定的資源文件了404,因此忽略了它一段時間。事實證明,缺少的文件會導致會話保持打開 - 從而左右製作StaleObjects。

因此作爲一個暗示,任何人面臨着比平常以上(一些StaleObjects可能總是發生 - 參見上面的答案)StaleObjectExceptions:確保所有資源是否正確鏈接,你的開發工具(鉻F12)不報告任何丟失的文件。

4

StaleObjectStateException可以自然發生在任何其他項目上。每次有兩個併發事務加載相同的實體版本,並且由於樂觀鎖定衝突故障,每個事務都會更改該實體,導致最終執行線程失敗。

你的情況有一個額外的呼叫,JPA邊界以外,這可能有利於這一問題:

Object o = PObject.lock(profileId) 

每個事務都是線裝,它發生在一個會話裏面,這樣兩個相互競爭的線程將繼續兩個對象引用爲同一個實體ID。樂觀鎖定目標在沒有任何其他顯式鎖定機制的情況下可以有效工作

如果發送修改後的實體參考saveObject版本,你仍然可以得到這個例外,它意味着你有一個高的機會爲兩個相互競爭的線程修改同一實體。

在這種情況下,pessimistic lock會產生更好的結果,因爲它涉及等待而不是optimistic fail-fast approach

+0

我更新了我的代碼。 .lock()調用不再存在。我仍然有20%左右的電話出現。它不是併發的(我正在單獨製作它作爲單個用戶,所以沒有併發的編輯) – 2014-09-03 15:09:11

5

StaleObjectStateException:

正如已經註釋掉了有關此異常。 在刷新(顯式或隱式)期間,當會話中存在無效的版本化域對象時,版本號會發生碰撞,但不會持久保存到數據庫。然後,當它再次生效並保存並刷新時,hibernate認爲它已過時,因爲版本號與數據庫中的版本不匹配,並且拋出StaleObjectStateException。

這可以解決像。

  1. 你有做更新之前找出版本值,如果是1,那麼你必須通過程序更新。在更新的特定操作。
  2. 通過使用@version註釋。

關於@version:

版本號機制樂觀鎖通過@版本註解提​​供。 示例:@版本註解

@Entity 
public class Flight implements Serializable { 
... 
    @Version 
    @Column(name="OPTLOCK") 
    public Integer getVersion() { ... } 
} 

這裏,版本屬性映射到OPTLOCK列,實體管理器使用它來檢測更新衝突,並防止因被覆蓋更新的損失last-commit-wins策略@version

有關Grails的更多詳細信息,請參閱下面的鏈接,它具有Git中心代碼。
test for GRAILS-8937: HibernateOptimisticLockingFailureException

+0

你能否詳細說明你的解決方案?我將如何使用@version註釋以及我將在第一個解決方案中更新哪些內容? – 2014-09-03 15:09:36

+1

@SebastianFlückiger:那些是解決這個異常的方法,查看關於hibernate框架的更多細節以瞭解版本的用法。我更新了一些關於版本的細節。其他給出的鏈接告訴Grails相關的解決方案。從表中更新,然後通過編寫代碼來更新該版本。 – Rudra 2014-09-03 16:08:57

相關問題