2013-07-09 141 views
1

有沒有辦法在之後收聽事件* * AfterTransactionCompletion(2次後)?Hibernate afterAfterTransactionCompletion攔截器

我工作的一個應用程序,用戶可以修改的實體並保存:

tx.begin(); 
entity.name = "Archimede"; 
em.merge(entity); 
tx.commit(); 

我想攔截的承諾,而「正確」的實體。假設:如果name.equal(「Archimede」),那麼攔截器應該改變surname =「Pitagorico」。

public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[]  previousState, String[] propertyNames, Type[] types) { 
    changedEntities.add(entity); //register all changing entities 
} 

public void afterTransactionCompletion(Transaction tx) { 
    for(T entity : changedEntities) { 
     if(entity.name.euqual("Archimede")) { 
      em.getTransaction().begin(); 
      entity.surname = "Pitagorico"; 
      sendNotificationToUser(); 
      em.getTransaction().commit(); 
     } 
    } 
} 

的問題是,它是不允許使用的EntityManager或交易的攔截,這將有類似失敗:

org.hibernate.TransactionException: reuse of Transaction instances not supported 

有罪的是在這裏(org.hibernate.engine。 transaction.spi.AbstractTransactionImpl):

@Override 
public void commit() throws HibernateException { 
    if (localStatus != LocalStatus.ACTIVE) { 
     throw new TransactionException("Transaction not successfully started"); 
    } 

    LOG.debug("committing"); 

    beforeTransactionCommit(); 

    try { 
     doCommit(); 
     localStatus = LocalStatus.COMMITTED; 
     afterTransactionCompletion(Status.STATUS_COMMITTED); //<------- 
    } 
    catch (Exception e) { 
     localStatus = LocalStatus.FAILED_COMMIT; 
     afterTransactionCompletion(Status.STATUS_UNKNOWN); 
     throw new TransactionException("commit failed", e); 
    } 
    finally { 
     invalidate(); //<------- 
     afterAfterCompletion(); //<------- 
    } 
} 

攔截由afterTransactionCompletion(Status.STATUS_COMMITTED)調用;,但是這是在之前invalidate();,並且該事務仍處於COMMIT狀態。 只要在下一行中調用afterAfterCompletion();,此時事務應該是有效的,並準備好.begin()

我想在這裏有一個攔截器方法,所以以前的代碼應該沒有任何問題。

任何人都知道一種方式?或者我的方法錯了? (任何更好的?)

+0

爲什麼你不使用@PrePersist註釋? – willome

+0

也在@PrePersist我不應該使用EntityManager或事務,並且我想只在事務實際提交時(而不是在回滾時)「糾正」實體。此外,交易不應該依賴於「更正」(如果更正失敗,只是更正交易失敗,而不是第一次)。他們應該是兩個分開的交易。 – lelmarir

+0

爲什麼我有看到不安全的併發代碼的感覺?將實體作爲實例字段使其變得糟糕。你怎麼看 ? –

回答

1

我會用一些AOP和將與@Transactions玩

例如:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
public MyEntity doSaveService(MyEntity myentity){ 
    em.merge(myentity); // not required but easier to read 
    em.flush(); //not sure if you need it 
} 

添加一些AOP在此合併方法(有一些註釋或XML聲明作爲你想要的)

@Transactional(propagation = Propagation.REQUIRES_NEW) 
protected Object postMergeProcess(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 
MyEntity myEntityBeforeMerge = (MyEntity) proceedingJoinPoint.getArgs()[0]; 
MyEntity myEntityAfterMerge = (MyEntity) proceedingJoinPoint.proceed(); 
myEntityAfterMerge.setWhatever("xxx"); 
em.merge(); 
} 

XML:

<aop:config> 
    <aop:aspect ref=""> 
    <aop:pointcut id="mergePointCut" expression="execution(* x.y.z.EntityService.merge(..))" /> 
      <aop:around method="postMergeProcess" pointcut-ref="mergePointCut" /> 
</aop:aspect> 
</aop:config> 
+0

謝謝,這可能工作。但我沒有得到合併方法,我不能只綁定EntityManager#merge()方法的建議?我必須對tx.commit()做出反應,但其原理相同。我使用谷歌guice,我希望它的AOP就足夠了。我現在唯一可以考慮的問題是管理實體更新,如果我沒有顯式調用merge(),是否有辦法強制我這樣做? – lelmarir

+0

編輯:我用明確的名稱更改合併方法。 AOP攔截應該在你的服務層或類似的東西上完成。你也可以試試Entity.merge。我不確定在這種情況下的行爲。 – willome

+0

我已經包裝了EntityManager,所以我可以攔截持久性,合併,刪除和trasaction.commit,但是,我剛剛意識到,截取合併,而不是onFlushDirty我失去currentState,previousState和propertyNames,所以現在我不能告訴哪些屬性已經改變。 (合併並不意味着實體已經改變了)。有任何想法嗎? – lelmarir