Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring @transaction not working as expected in junit in non debug mode

All MyService methods are transactional. The junit test below, gets count of items, saves a new item, and gets count of items to make sure that counts has been incremented by 1.

public class MyTest extends ServiceTest{

    1. int countBefore = myService.getCount();    //return n
    2. myService.add(item);                       //item is really added to DB
    3. int countAfter  = myService.getCount();    //return n (sometimes n+1)
}

@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED)
getCount(){…}

@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.SERIALIZABLE)
add(){…}

@Ignore
@ContextConfiguration(locations = { "file:src/main/resources/xxx-context.xml", 
                                    "file:src/main/resources/xxx-data.xml", 
                                    "file:src/main/resources/xxx-services.xml"  })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
@TestExecutionListeners( {  DependencyInjectionTestExecutionListener.class,
                            DirtiesContextTestExecutionListener.class,
                            TransactionalTestExecutionListener.class,
                            TestListener.class})

public class ServiceTest extends AbstractUT{

@Ignore
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners( {TestListener.class})
public class AbstractUT{

When debugging (3.) returns n+1 which is what I want. But when running the test without debug I get n.

Even sometimes when running the test I get n+1 and next time I get n and when comparing the std output between the two execution, it looks exactly the same. I have enabled log4j.logger.org.springframework.transaction=TRACE and I can see:

Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization

...

Initializing transaction synchronization
Getting transaction for MyService.add
...
Completing transaction for MyService.add
Clearing transaction synchronization

...

Initializing transaction synchronization
Getting transaction for MyService.getCount
...
Completing transaction for MyService.getCount
Clearing transaction synchronization

So transactions are being executed one after the other, but how is possible that (3.) don't see the saved item?

Transaction managment is setup in my test class as per: https://stackoverflow.com/a/28657650/353985

How can I find what is going wrong? Thanks!

like image 490
redochka Avatar asked Feb 23 '15 08:02

redochka


People also ask

Is @transactional required in Spring boot?

When the propagation is MANDATORY, if there is an active transaction, then it will be used. If there isn't an active transaction, then Spring throws an exception: @Transactional(propagation = Propagation. MANDATORY) public void mandatoryExample(String user) { // ... }

How is @transactional implemented in Spring?

Transactions and Proxies. At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.

Why @transactional annotation is used in Spring?

So when you annotate a method with @Transactional , Spring dynamically creates a proxy that implements the same interface(s) as the class you're annotating. And when clients make calls into your object, the calls are intercepted and the behaviors injected via the proxy mechanism.

What happens when a method annotated with @transaction propagation Requires_new is invoked?

The main difference between them is if a method in Spring Business Activity/DAO class is annotated with Propagation. REQUIRES_NEW, then when the execution comes to this method, it will create a new transaction irrespective of the existing transaction whereas if the method is annotated with Propagation.


1 Answers

Had similar issue, but in my case it did not rollback. It seems that you forgot to add @Transactional. From documentation (link)

Transaction management

In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener which is configured by default, even if you do not explicitly declare @TestExecutionListeners on your test class. To enable support for transactions, however, you must configure a PlatformTransactionManager bean in the ApplicationContext that is loaded via @ContextConfiguration semantics (further details are provided below). In addition, you must declare Spring’s @Transactional annotation either at the class or method level for your tests.

Here is example form the link above.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
@Transactional
public class FictitiousTransactionalTest {

@BeforeTransaction
public void verifyInitialDatabaseState() {
    // logic to verify the initial state before a transaction is started
}

@Before
public void setUpTestDataWithinTransaction() {
    // set up test data within the transaction
}

@Test
// overrides the class-level defaultRollback setting
@Rollback(true)
public void modifyDatabaseWithinTransaction() {
    // logic which uses the test data and modifies database state
}

@After
public void tearDownWithinTransaction() {
    // execute "tear down" logic within the transaction
}

@AfterTransaction
public void verifyFinalDatabaseState() {
    // logic to verify the final state after transaction has rolled back
}

}

like image 168
kyla Avatar answered Oct 04 '22 00:10

kyla