2014-02-07 107 views
2

我的Grails應用程序有一個服務方法,它可以更新last.fm的web服務中的藝術家列表。Grails編程事務處理

@Transactional(propagation = Propagation.NOT_SUPPORTED) 
void updateLastFmArtists(Range idRange = null) { 

    Artist.list().each { Artist artist -> 

     // We could be updating a lot of artists here, the process could take up 
     // to an hour and we don't want to wrap all that in a single transaction 
     Artist.withTransaction { status -> 
      try { 
       updateArtistInfo(artist) 

      } catch (IOException ex) { 
       status.setRollbackOnly() 
      } 
     } 
    } 
} 

每個個體藝術家它自己的事務,如果IOException拋出應當回滾內更新。不過,我注意到以下行爲:

如果要更新一個藝術家試圖拋出一個IOException - 導致事務回滾 - 那麼接下來藝術家的更新總是失敗,因爲以下錯誤

org.hibernate.LazyInitializationException:無法初始化懶洋洋地一 角色的集合:org.example.Artist.topTracks,沒有會話或 會話關閉

如果我改變上面的代碼,使每一位藝術家被更新w^ithin它自己的會話,這似乎是解決問題,

Artist.withNewSession { session -> 
     Artist.withTransaction { status -> 
      try { 
       updateArtistInfo(artist) 

      } catch (IOException ex) { 
       status.setRollbackOnly() 
      } 
     } 
    } 

但我不明白爲什麼我要做到這一點,即這是爲什麼回滾事務似乎關閉會話?

回答

4

回滾會導致會話不可用,這是正常的,因爲它是像所有Hibernate異常一樣的不可恢復的錯誤。參見例如ObjectNotFoundException類的Javadoc:

/* 
* ... 
* 
* Like all Hibernate exceptions, this exception is considered 
* unrecoverable. 
* 
*/ 

的原因是,該會話是在存儲器中的數據庫和對象之間的狀態同步器組件。處理數據庫中的回滾的方法是回滾內存中對象的更改。

由於此功能難以實施並且使用有限,因此決定採取措施使這些類型的異常無法恢復。

您可以嘗試捕捉它並繼續使用會話,但不能保證會話處於一致狀態。

編輯:

這裏比的Javadoc其他更多參考資料,在documentation發現:

由Hibernate拋出的異常意味着你必須立即回滾您 數據庫事務,並關閉Session立即(這是 在本章後面更詳細討論)。如果您的會話綁定到應用程序的 ,則必須停止應用程序。滾動 返回數據庫事務並不會將您的業務對象返回到 處於事務開始時的狀態。這意味着 數據庫狀態和業務對象將不同步。 通常這不是問題,因爲異常不可恢復 ,無論如何您必須重新開始。

也:

如果Session拋出異常,包括任何的SQLException, 立即回滾數據庫事務,調用Session.close() ,丟棄該Session實例。會話的某些方法不會 使會話保持一致狀態。 沒有拋出異常Hibernate可以被視爲可恢復。通過在finally塊中調用close()來確保會話將會關閉 。

+0

+1但是,在這種特殊情況下,它不是一個無法解決的異常,這是我在單個事務範圍內捕捉的一個例外。我覺得這很令人吃驚,這會使整個會話無效 –

+0

我增加了一些對此的引用,基於文檔的建議是重新開始新會話,因爲會話的某些方法不會使會話保持一致狀態在回滾的情況下 –

+0

感謝您的幫助,但是,上面的引用是關於Hibernate或Hibernate異常拋出的異常,但我的情況並不涉及這兩種異常。我猜這是我對'status.setRollbackOnly()'的調用,它會使會話失效而不是拋出的異常 –