2011-06-27 23 views
3

我使用Spring/JPA2 /這段代碼冬眠:間接的Hibernate/JPA的方法調用失去交易

class A { 
    @Autowired 
    B b; 

    @RequestMapping("/test") 
    public void test(final HttpServletRequest r1, HttpServletResponse r2) throws ... { 

    b.inner(); // Works 

    b.outer(); // javax.persistence.TransactionRequiredException: 
       // no transaction is in progress ... :| 
} 

@Component 
class B { 
    @PersistenceContext 
    EntityManager em; 

    public void outer() { inner(); } 

    @Transactional 
    public void inner() { em.flush(); } 
} 

爲什麼inner()只有當間接地稱爲鬆散的交易?

回答

5

http://static.springsource.org/spring/docs/current/reference/transaction.html#transaction-declarative-annotations

在代理模式中,彈簧的文檔(它是默認值),只有外部方法調用通過代理來在被截獲。這意味着即使被調用的方法標記爲@Transactional,實際上,自調用目標對象內的方法調用目標對象的另一個方法也不會導致實際的事務處理。

如果您希望自我調用也包含事務,請考慮使用AspectJ模式(請參閱下表中的模式屬性)。在這種情況下,首先不會有代理;相反,目標類將被編織(即,其字節碼將被修改),以便將@Transactional轉換爲任何類型方法的運行時行爲。

@Autowired參考B b(內側A類),是包裹着一個Spring AOP事務感知代理。

當調用b.inner()時,您在事務感知實例上調用它,並將其標記爲@Transactional。因此,Spring管理的事務就開始了。

b.outer()被調用,它也是一個交易意識的實例,但它是不@Transactional。因此一個Spring管理的交易是而不是開始。

一旦你的outer()調用調用inner()通過交易感知代理不往裏走,它被直接調用。它與this.inner()相同。既然你直接調用它,而不是通過代理,它沒有Spring事務感知語義。

由於沒有交易已開始,因此導致TransactionRequiredException

可能的解決方案包括製作方法outer()@Transactional

@Transactional 
    public void outer() { inner(); } 

或使整個班級@Transactional

@Transactional 
@Component 
class B { 
    @PersistenceContext 
    EntityManager em; 
+0

我的目標是創建一個後臺程序並每次提交一些小的位。所以註釋outer()作爲單數事務不是我的意圖。我正在將代理模式更改爲aspect-j模式。 謝謝! – Cojones

+0

如果'B.outer()'中的邏輯需要在'B.inner()'之前發生,但不涉及事務/持久性,它可能不屬於你的持久層類(DAL/DAO等)。考慮重構你的設計來分離問題,可能通過使用中間對象。 'Object intermediate = b.outer(); b.inner(中間);'。然後重構,以便它不再是持久層類的成員。 'Object intermediate = someOtherUtilityOrSupportClassInstance.outer(); b.inner(中間);'。 – Dan

+0

現在我已經切換到LTW了,但我現在在任何調用中都沒有交易:(所有配置都添加了 Cojones

0

事務性上下文持續超出Spring bean整個生命週期的範圍。 @Transactional符號具有整個組件的範圍,你應該註釋你的@Component作爲@Transactional如

@Transactional 
@Component 
class B { 
    @PersistenceContext 
    EntityManager em; 

    public inner() { } 
    public outer() { } 
} 

方法內外應完成的工作的各個單元。如果你需要一些輔助函數或者你有什麼好的方法,但是需要事務邊界的工作單元應該限定在每個方法的範圍內。參見@Transactional http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html