2011-08-22 302 views
2

我使用@Transactional我的JUnit測試(主要優勢是一個測試中改變的回滾),但我有個小問題,這影響我的服務交易。因此,例如這是春季測試inpact上服務交易

我的服務:

@Service 
@Transactional(propagation = Propagation.REQUIRED) 
public class ServiceImpl 

我的單元測試:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = { "/test-context.xml" }) 
@Transactional 
public class TestService 

@Test 
public void testNumberTransaction() { 
Entity a = new Entity(); 
Entity b = new Entity(); 
service.add(a); 
service.add(b); 
} 

所以我天真地期望有service.add()兩個獨立的交易記錄,但除非我用@它在一個事務內部運行的測試方法上是非事務性的(但在測試之後它不會回滾)。

這是預期嗎?我可以通過一些配置改變它嗎?

謝謝

回答

4

是的,它是預期的。 Propagation.REQUIRED(這是默認值)表示:如果存在,則在現有事務中執行。否則,創建一個事務並在方法結束時提交它。

所以,是的,如果整個測試方法是事務性的,既服務電話將在測試事務的上下文中執行。

注意,由於服務與REQUIRED註解,它應該如果事務已經存在的工作。這使測試有效:它在現有事務的上下文中測試您的服務。如果您希望服務在自己的專用交易中執行,則應該使用REQUIRES_NEW註釋。但是,當然,如果是這種情況,您將無法通過在事務中執行測試來回滾服務事務。

2

默認情況下(REQUIRES是默認的傳播行爲)如果交易已經存在,你的服務方法不會產生新的,而是加盟存在的一個。在你的情況下,它是由Spring測試框架(將回滾的)創建的事務。

您可以更改設置傳播到REQUIRES_NEW此行爲。但是因爲現在您的業務add方法在新事務中運行,所以一旦離開此方法,事務將被提交而不是回滾。默認情況下,由於所有事務都在JUnit測試事務中加入,所有對數據庫所做的更改都會回滾。

1

@Transactional定義了分界線。註釋該類相當於註釋了該類的每個公共方法,但分界線仍然是方法(在您的案例中,爲testNumberTransaction())。提交/回滾決定將在分界點進行,即從測試方法返回時。無論您指定REQUIRESREQUIRES_NEW傳播,實際成交單位是一樣的,你testNumberTransaction()方法,這樣兩個service.add(...)調用將始終在同一事務執行。

如果您的目標是在測試後始終回滾您的交易,那麼只需刪除@Transactional註釋(或者像您提到的那樣放置@nontransactional)即可。

如果,另一方面,要強制一個新的交易,每個service.add(...)調用,您既可以創建服務類的包裝,你有一個add(...)方法標註有@Transactional(傳播=傳播。 REQUIRES_NEW)並從那裏調用包裝的service實例add(...)方法。或者您可以在您的彈簧測試環境中添加一些聲明式事務管理,爲您的service.add(...)方法添加事務通知。有關如何使用<tx:XXX>標籤添加聲明性事務支持,請參閱spring documentation

+1

從測試方法中刪除@Transactional不會回退事務:測試不會有任何事務。事務將在服務方法被調用時開始,並在服務方法返回時提交。 –

+0

真實,笨拙的表述。刪除@Transactional註釋將會阻止事務管理器在java層創建一個事務。但是,大多數數據庫將始終爲每個會話創建一個隱式事務,並且如果將自動提交設置爲關閉(默認爲IIRC),則沒有事務的行爲與始終回滾的事務的行爲相同。如果我錯了,請糾正我。 – pap

+0

錯誤,沒有。從* test *方法中移除Transactional將使測試在任何事務上下文之外運行,但Spring將仍然爲每個服務方法調用啓動事務,因爲* service *方法使用Transactional註釋。由服務完成的實際事務工作(可能更新某個數據庫)將在事務中完成並提交,除非服務本身拋出運行時異常。 –