2013-04-02 72 views
17

這裏是我的問題:使用@Transactional(傳播= Propagation.REQUIRES_NEW)奇怪的行爲

我運行一個Java EE /春/ Hibernate應用程序的批次。該批次調用method1。此方法調用method2,這可能會導致UserException(類別延伸RuntimeException)。下面是它的樣子:

@Transactional 
public class BatchService implements IBatchService { 
@Transactional(propagation=Propagation.REQUIRES_NEW) 
public User method2(User user) { 
    // Processing, which can throw a RuntimeException 
} 

public void method1() { 
    // ... 
    try { 
    this.method2(user); 
    } catch (UserException e) { 
    // ... 
    } 
    // ... 
} 
} 

異常被逮住的繼續執行,但在method1當交易被關閉的RollbackException結束時拋出。

這裏是堆棧跟蹤:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly 
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:476) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754) 
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120) 
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
at $Proxy128.method1(Unknown Source) 
at batch.BatchController.method1(BatchController.java:202) 

method2不拋出此異常,它工作得很好。

我曾嘗試:

  • method2

method1

  • try和catch設置@Transactional(noRollbackFor={UserException.class}))但它並沒有改變任何東西。

    由於異常在不同的事務,其中回滾發生,我不明白爲什麼它不工作異常。我看過這個:Jpa transaction javax.persistence.RollbackException: Transaction marked as rollbackOnly,但它並沒有真正幫助我。

    我會很感激,如果有人可以給我一個線索。

    更新

    我將它通過對method2稱爲(實際上是被髮送異常的一種)的方法設置propagation=Propagation.REQUIRES_NEW工作。這個方法在類中定義,類似於我的BatchService。所以我不明白它爲什麼在這個級別上工作,而不是method2

    • 我設置method2公衆作爲註釋@Transactional沒有考慮到,如果如文檔中說的方法是私有的:

    @Transactional註解可以在之前被放置接口 定義,接口上的方法,類定義或公共方法。

    • 我還試圖用Exception代替RuntimeException(因爲它是比較合適的),但它也沒有任何改變。

    即使它正在工作,問題仍然存在,因爲它有一個奇怪的行爲,我想明白爲什麼它沒有像它應該那樣行事。

  • +0

    見http://stackoverflow.com/questions/5152686/self-injection-with-spring/可能的解決方法。 – Vadzim

    回答

    35

    Spring的事務中,默認情況下,通過包裝的Spring bean與處理事務和異常的代理工作。當您從method1()method2(),你完全繞過了這個代理,所以它不能啓動新的事務,而你有效地從相同的事務由一個調用打開method1()調用method2()

    相反,當您從method1()調用另一個注入bean的方法時,實際上是調用事務代理的方法。因此,如果這個外來的方法標有REQUIRES_NEW,一個新的事務由代理起步,你能趕上例外method1(),恢復外部事務。

    the documentation進行說明。

    +0

    真正理解發生了什麼的完美答案,謝謝! – DessDess

    +2

    請注意,如果您需要這些自調用功能,則應該在Spring中使用AspectJ模式。見http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html:「考慮使用AspectJ的模式(參見下表的模式屬性),如果你希望自調用在這種情況下,首先不會有代理;相反,目標類將被編織(即,其字節代碼將被修改),以便將@Transactional轉換爲運行時在任何一種方法上的行爲。「 –

    +0

    是的,真正瞭解春季交易......謝謝 – Vito