jpa 异常捕获_JPA和CMT –为什么捕获持久性异常不够?
jpa 異常捕獲
使用CMT( 容器管理的事務 )進入EJB和JPA的世界非常舒適。 只需定義一些注釋來劃分事務邊界(或使用默認值)即可,僅此而已–無需擺弄手動開始,提交或回滾操作。 回滾事務的一種方法是從EJB的業務方法中引發非應用程序異常(或具有rollback = true的應用程序異常)。 看起來很簡單:如果在某些操作過程中可能會引發異常,并且您不想回滾tx,那么您應該捕獲該異常就可以了。 現在,您可以在同一仍處于活動狀態的事務中再次重試該易失性操作。
現在,對于從用戶組件拋出的應用程序異常,這一切都是正確的 。 問題是– 除了其他組件引發的異常之外,還有什么? 就像JPA的EntityManager拋出PersistenceException ? 這就是故事的開始。
我們想要實現的目標
設想以下情形:您有一個名為E的實體。它包含:
- id –這是主鍵,
- 名稱 -這是一些易于理解的實體名稱,
- 內容 –包含字符串的任意字段–它模擬“高級屬性”,例如,在持久性/合并期間進行計算會導致錯誤。
- 代碼 –包含OK或ERROR字符串–定義高級屬性是否成功持久保存,
您要持久化E。您假定E的基本屬性將始終被成功持久化。 但是,高級屬性需要一些額外的計算或操作,這可能會導致例如從數據庫引發約束沖突。 如果發生這種情況,您仍然希望E保留在數據庫中(但僅填充基本屬性,并且將代碼屬性設置為“ ERROR”)。
換句話說,這是您可能想到的:
天真的解決方案
轉到EJB的代碼,這就是您可以嘗試執行的方式(假設使用默認的TransactionAttributes):
public void mergeEntity() {MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');em.persist(entity);// This will raise DB constraint violationentity.setContent('tooLongContentValue');// We don't need em.merge(entity) - our entity is in managed mode.try {em.flush(); // Force the flushing to occur now, not during method commit.} catch (PersistenceException e) { // Clear the properties to be able to persist the entity.entity.setContent('');entity.setCode('ERROR');// We don't need em.merge(entity) - our entity is in managed mode.} }這個例子有什么問題?
捕獲由EntityManager拋出的PersistenceException 不會阻止事務回滾 。 并不是在EJB中不緩存異常將tx標記為回滾。 這是EntityManager 拋出的非應用程序異常 ,將tx標記為回滾。 更不用說資源本身可能會在內部將tx標記為回滾。 這實際上意味著您的應用程序實際上無法控制此類tx行為。 此外,由于事務回滾,我們的實體已移至分離狀態。 因此,在此方法末尾需要一些em.merge(entity) 。
工作方案
那么如何處理這種自動事務回滾? 因為我們正在使用CMT,所以唯一的方法是定義另一種業務方法,該方法將啟動新事務并在那里執行所有易碎的操作 。 這樣,即使將拋出(并捕獲) PersistenceException ,它也將僅標記要回滾的新事務。 我們的主要TX將保持不變。 在下面,您可以從此處看到一些代碼示例(為簡潔起見,刪除了日志記錄語句):
public void mergeEntity() {MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');em.persist(entity);try {self.tryMergingEntity(entity);} catch (UpdateException ex) {entity.setContent('');entity.setCode('ERROR');} }@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void tryMergingEntity(final MyEntity entity) throws UpdateException {entity.setContent('tooLongContentValue');em.merge(entity);try {em.flush();} catch (PersistenceException e) {throw new UpdateException();} }注意:
- UpdateException是@ApplicationException ,它擴展了Exception(因此默認情況下為rollback=false )。 用于通知更新操作失敗。 或者,您可以更改tryMergingEntity(-)方法簽名以返回布爾值而不是void。 此布爾值可以描述更新是否成功。
- self是對我們自己的EJB的自我引用。 這是使用EJB容器代理的必需步驟,該代理使被調用方法的@TransactionAttribute起作用。 或者,您可以使用SessionContext#getBusinessObject(clazz).tryMergingEntity(entity) 。
- em.merge(entity)是至關重要的。 我們正在tryMergingEntity(-)中開始新事務,因此該實體不在持久性上下文中。
- 此方法不需要任何其他合并或刷新。 tx尚未回滾,因此批準了CMT的常規功能,這意味著在tx提交期間將自動刷新對實體的所有更改。
讓我們再次強調最重要的一點: 如果您捕獲到異常,并不表示您當前的事務沒有被標記為回滾。 PersistenceException不是ApplicationException,即使您是否捕獲它,也將使您的tx回滾。
JTA BMT解決方案
我們一直在談論CMT。 JTA BMT呢? 好吧,作為一個好處,找到下面的代碼,該代碼顯示如何使用BMT處理此問題(也可在此處訪問):
public void mergeEntity() throws Exception {utx.begin();MyEntity entity = new MyEntity('entityName', 'OK', 'DEFAULT');em.persist(entity);utx.commit();utx.begin();entity.setContent('tooLongContentValue');em.merge(entity);try {em.flush();} catch (PersistenceException e) {utx.rollback();utx.begin();entity.setContent('');entity.setCode('ERROR');em.merge(entity);utx.commit();} } 使用JTA BMT,我們可以用一種方法來完成所有這一切。 這是因為我們控制著tx何時開始以及提交/回滾 (看看那些utx.begin()/ commit()/ rollback()。盡管如此,結果還是一樣的–拋出PersistenceException我們的tx被標記為回滾然后您可以使用UserTransaction#getStatus()進行檢查,并將其與諸如Status.STATUS_MARKED_ROLLBACK之類的常量進行比較,并可以在我的GitHub帳戶上檢查整個代碼。
參考: JPA和CMT –為什么捕獲持久性異常不足? 從我們的JCG合作伙伴 Piotr Nowicki在Piotr Nowicki的首頁博客中獲得。
翻譯自: https://www.javacodegeeks.com/2013/03/jpa-and-cmt-why-catching-persistence-exception-is-not-enough.html
jpa 異常捕獲
總結
以上是生活随笔為你收集整理的jpa 异常捕获_JPA和CMT –为什么捕获持久性异常不够?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为s7平板电脑(华为s8-701w平板
- 下一篇: 使用Spring @Transactio