JPA和CMT –为什么捕获持久性异常不够?
使用CMT( 容器管理的事務(wù) )進(jìn)入EJB和JPA的世界非常舒適。 只需定義一些注釋來劃分事務(wù)邊界即可(或使用默認(rèn)值),僅此而已–無需擺弄手動(dòng)開始,提交或回滾操作。 回滾事務(wù)的一種方法是從EJB的業(yè)務(wù)方法中引發(fā)非應(yīng)用程序異常(或具有rollback = true的應(yīng)用程序異常)。 看起來很簡(jiǎn)單:如果在某些操作過程中可能會(huì)引發(fā)異常,并且您不想回滾tx,那么您應(yīng)該捕獲該異常就可以了。 現(xiàn)在,您可以在同一仍處于活動(dòng)狀態(tài)的事務(wù)中再次重試該易失性操作。
現(xiàn)在,對(duì)于從用戶組件拋出的應(yīng)用程序異常,這一切都是正確的 。 問題是– 除了其他組件引發(fā)的異常之外,還有什么? 就像JPA的EntityManager拋出PersistenceException ? 這就是故事的開始。
我們想要實(shí)現(xiàn)的目標(biāo)
設(shè)想以下情形:您有一個(gè)名為E的實(shí)體。它包含:
- id –這是主鍵,
- 名稱 -這是一些易于理解的實(shí)體名稱,
- 內(nèi)容 -包含字符串的任意字段-它模擬“高級(jí)屬性”,例如,在持久性/合并期間進(jìn)行計(jì)算會(huì)導(dǎo)致錯(cuò)誤。
- 代碼 –包含OK或ERROR字符串–定義高級(jí)屬性是否成功持久保存,
您要持久化E。您假定E的基本屬性將始終被成功持久化。 但是,高級(jí)屬性需要一些額外的計(jì)算或操作,這可能會(huì)導(dǎo)致例如從數(shù)據(jù)庫(kù)引發(fā)約束沖突。 如果發(fā)生這種情況,您仍然希望E保留在數(shù)據(jù)庫(kù)中(但僅填充基本屬性,并且將代碼屬性設(shè)置為“ ERROR”)。
換句話說,這就是您可能想到的:
天真的解決方案
轉(zhuǎn)到EJB的代碼,這就是您可以嘗試執(zhí)行的方式(假設(shè)使用默認(rèn)的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.} }這個(gè)例子有什么問題?
捕獲由EntityManager拋出的PersistenceException 不會(huì)阻止事務(wù)回滾 。 并不是在EJB中不緩存異常會(huì)使tx標(biāo)記為回滾。 這是EntityManager 拋出的非應(yīng)用程序異常 ,將tx標(biāo)記為回滾。 更不用說資源可能會(huì)在其內(nèi)部將tx標(biāo)記為回滾。 這實(shí)際上意味著您的應(yīng)用程序?qū)嶋H上無法控制此類tx行為。 此外,由于事務(wù)回滾,我們的實(shí)體已移至分離狀態(tài)。 因此,此方法末尾需要一些em.merge(entity) 。
工作方案
那么如何處理這種自動(dòng)事務(wù)回滾? 因?yàn)槲覀兪褂玫氖荂MT,所以唯一的方法是定義另一種業(yè)務(wù)方法,該方法將啟動(dòng)新的事務(wù)并在那里執(zhí)行所有易碎的操作 。 這樣,即使將拋出(并捕獲) PersistenceException ,它也將僅標(biāo)記要回滾的新事務(wù)。 我們的主要TX將保持不變。 在下面,您可以從此處看到一些代碼示例(為簡(jiǎn)潔起見,刪除了日志記錄語句):
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 ,它擴(kuò)展了Exception(因此默認(rèn)情況下為rollback=false )。 用于通知更新操作失敗。 或者,您可以更改tryMergingEntity(-)方法簽名以返回布爾值而不是void。 該布爾值可以描述更新是否成功。
- self是對(duì)我們自己的EJB的自我引用。 這是使用EJB容器代理的必需步驟,該代理使被調(diào)用方法的@TransactionAttribute起作用。 或者,您可以使用SessionContext#getBusinessObject(clazz).tryMergingEntity(entity) 。
- em.merge(entity)是至關(guān)重要的。 我們正在tryMergingEntity(-)中開始新事務(wù),因此該實(shí)體不在持久性上下文中。
- 此方法不需要任何其他合并或刷新。 tx尚未回滾,因此批準(zhǔn)了CMT的常規(guī)功能,這意味著在tx提交期間將自動(dòng)刷新對(duì)實(shí)體的所有更改。
讓我們?cè)俅螐?qiáng)調(diào)最重要的一點(diǎn): 如果您捕獲到異常,這并不意味著您的當(dāng)前事務(wù)沒有被標(biāo)記為回滾。 PersistenceException不是ApplicationException,即使您是否捕獲它,也將使您的tx回滾。
JTA BMT解決方案
我們一直在談?wù)揅MT。 JTA BMT呢? 好吧,作為獎(jiǎng)勵(lì),請(qǐng)找到以下代碼,該代碼顯示了如何使用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,我們可以用一種方法完成所有這一切。 這是因?yàn)槲覀兛刂浦鴗x何時(shí)開始以及提交/回滾 (看看那些utx.begin()/ commit()/ rollback()。盡管如此,結(jié)果還是一樣的–拋出PersistenceException我們的tx被標(biāo)記為回滾然后可以使用UserTransaction#getStatus()進(jìn)行檢查,并將其與諸如Status.STATUS_MARKED_ROLLBACK之類的常量進(jìn)行比較,并可以在我的GitHub帳戶上檢查整個(gè)代碼。
參考: JPA和CMT –為什么捕獲持久性異常不足? 從我們的JCG合作伙伴 Piotr Nowicki在Piotr Nowicki的首頁(yè)博客中獲得。
翻譯自: https://www.javacodegeeks.com/2013/03/jpa-and-cmt-why-catching-persistence-exception-is-not-enough.html
總結(jié)
以上是生活随笔為你收集整理的JPA和CMT –为什么捕获持久性异常不够?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑如何和PLC建立连接plc如何与电脑
- 下一篇: TPlink路由器如何设置流量分配如何设