2012-09-17 167 views
3

我試圖實現異常處理的休眠引發的樂觀鎖類​​型異常,但我遇到了一個奇怪的問題。看來我無法抓住任何Gorm異常。如何處理GORM異常

例如,我有這樣的代碼在我的服務:

try { 
    User user = User.get(1); 
    Thread.sleep(10000); 
    user.viewedAt(new Date()); 
    user.save(flush:true); 
} catch (OptimisticLockingException ex) { 
    log.error("Optimistic lock exception"); 
} catch (StaleObjectStateException ex) { 
    log.error("Optimistic lock exception"); 
} 

當我打這個區塊有兩個線程,其炸燬和異常傳播到Grails的標準異常處理程序。即使報告的例外是StaleObjectStateException,catch塊也不會被調用。

我注意到我可以捕捉異常,如果我讓它傳播到控制器並在那裏捕捉它,但似乎我不能在奇怪的服務實現異常處理。

我錯過了什麼?

回答

5

我得到了這個底部,我張貼它,以防其他人遇到這種情況。發生該問題是因爲try/catch塊處於事務性服務中。雖然grails報告在save()調用期間拋出異常,但事實上,在事務提交時,它被稱爲AFTER整個方法。

如此看來:

  1. flush: true對交易服務沒有影響
  2. 這是不可能趕上交易服務格姆相關的例外,至少在沒有一些工作

我最後通過自己手動管理交易來解決這個問題,例如

try { 
    User.withNewTransaction { 
    User user = User.get(id); // Must reload object 
    .. // do stuff 
    user.save(flush:true) 
    } 
} catch (OptimisticLockingException ex) { 
    ... 
} 

我希望這對別人有用!

+0

感謝您分享您找到的和您的解決方案。這看起來相當反直覺,所以如果您覺得慈善,請將您發現的內容發佈到Grails郵件列表中,以便糾正行爲或者在此問題上添加更好的文檔。 – cdeszaq

+1

當然,我會這樣做 –

1

我花了一些時間研究這個問題,並編寫了一個更完整的解決方案來處理Grails中樂觀鎖定異常的情況。首先,雖然堆棧跟蹤中報告的異常是StaleObjectStateException,但實際引發的異常是HibernateOptimisticLockingFailureException(而不是「OptimisticLockingException」)。其次,如果你想推廣這個來處理修改域對象的任意閉包,你需要重新拋出閉包內拋出的異常。

下面的靜態函數需要一個對象,該對象上運行的關閉,保存它,如果它失敗了,再次重試,直到它成功:在這種情況下

public static retryUpdate(Object o, Closure c) throws Exception { 
    def retVal 
    int retryCount = 0 
    while (retryCount < 5) { 
     try { 
      Model.withTransaction { status -> 
       retVal = c(status) 
       o.save() 
      } 
      return retVal 
     } catch (HibernateOptimisticLockingFailureException e) { 
      log.warn "Stale exception caught saving " + o 
      if (++retryCount >= 3) { // if retry has failed three times, pause before reloading 
       Thread.sleep(1000) 
      } 
      o.refresh() 
     } catch (UndeclaredThrowableException e2) { 
      // rethrow exceptions thrown inside transaction 
      throw e2.getCause() 
     } 
    } 

    return null 
} 

型號爲任何GORM模型上課,哪一個並不重要。特別是它是否是傳入對象的類別無關緊要。使用

例子:

AnotherModelClass object = AnotherModelClass.get(id) 
retryUpdate(object) { 
    object.setField("new value") 
} 
+0

我想知道這裏是否有其他事情發生。我在非事務方法(既沒有服務類也沒有標記事務的方法)中試過這個解決方案,它仍然拋出異常,然後傳播。 –

0

我就遇到了這個問題,並且適合我的需要的解決方法是添加一個對象。在操作對象之前刷新(),以便在db中獲得最新的數據。類似的東西:

User user = User.get(1); 
Thread.sleep(10000); 

//workaround here 
user.refresh() 

//manipulation here 
user.viewedAt(new Date()); 
user.save(flush:true); 

希望這會有所幫助,歡呼!