5

Here's my test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:repositoryContextTest.xml" })
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class SeasonITest {
@Autowired
private SeasonDao seasonDao;

@Test
public void createSeason() throws Exception {
    Season season = new Season();
    season.setName("2012");
    seasonDao.createSeason(season);
}

and the dataSource in my bean configuration file

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost/tournament_system" />
    <property name="username" value="root" />
    <property name="password" value="root" />
    <property name="defaultAutoCommit" value="false"/>
    <property name="poolPreparedStatements" value="false"/>
    <property name="maxOpenPreparedStatements" value="0"/>
 </bean>

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

When I run this test there a new record is created in my database.

How can I rollback this transaction?

This is the log output I see:

2012-06-15 15:00:02,173 [main] INFO  - ionalTestExecutionListener - 
 Rolled back transaction after test execution for test context 
 [[TestContext@76db09 testClass = SeasonITest, 
   locations = array<String>['classpath:repositoryContextTest.xml'], 
   testInstance = org.toursys.repository.dao.SeasonITest@1265109, 
   testMethod = createSeason@SeasonITest, testException = [null]]]

UPDATE:

all answers below want to change logic or database engine what I dont want. So I am offering reputation point to the right answer:

Why when I have this: @TransactionConfiguration(defaultRollback = true) in transaction configuration tests are not rolled back and how I can fix it ?

hudi
  • 15,555
  • 47
  • 142
  • 246

9 Answers9

7

If you are using MySQL with MyISAM engine try switching to InnoDB.

For more complicated test you will probably need a mocking framework or DB recreation.

EDIT1: According to the documentation InnoDB is transactional with full ACID support while MyISAM has support for atomic operations. More reading: Transaction and Atomic Operation Differences

EDIT2: In @TransactionConfiguration the default value for defaultRollback is true, so instead of commenting the line you should add @TransactionConfiguration(defaultRollback=false)

Krystian Lieber
  • 481
  • 3
  • 10
  • hm thx a lot it work and what is the different between InnoDB and MyISAM ? – hudi Jun 15 '12 at 13:23
  • hm but after I change engine. and comment defaultRollback = true then this record isnt save in my db – hudi Jun 15 '12 at 13:31
  • @hudi did setting defaultRollback explicity to false helped? – Krystian Lieber Jun 15 '12 at 15:05
  • yes thx a lot I forget that default value is true so when I set to false I can see new record but why rolback doesn work in MIsam engine ? – hudi Jun 20 '12 at 12:34
  • According to the documentation MyISAM is a nontransactional database engine, so it doesn't support transactions. That's why rollback is not working. I think you can simulate begin transaction->...->commit by locking the table, but as long as all rows are saved on the fly you cant rollback. For further explanation please read the documentation following the link provided. – Krystian Lieber Jun 21 '12 at 12:58
1

On the surface, your code and configuration looks correct. However, could you please post your SeasonsDAO object. We would first need to verify that the SeasonsDAO is correctly configured for transactions.

SeasonsDAO needs to be participating in the same TransactionContext as your test case and there is no way to verify that with the code you posted.

Did you mark SeasonsDAO as @Transactional ?

If it is not, I am not sure how the TransactionProxyFactoryBean would be able to proxy and declaratively manage the transaction.

At a high level, spring uses the notion of proxying to perform many services such as transaction management.

If you mark something as @Transactional, Spring will dynamically create a proxy that will implement the same interfaces as your target class. Once it is proxied, a transaction interceptor will wrap method calls to your target class and decide whether to rollback or commit based on defaults or rules that you may specify.

Roy Kachouh
  • 1,855
  • 16
  • 24
  • Then I am pretty sure that your SeasonsDAO is not running in a transaction, and therefore will not rollback an insert. – Roy Kachouh Jun 20 '12 at 13:34
  • question is answered and test are anotated as transactional so I think it is enought – hudi Jun 20 '12 at 13:36
0

There's also other solutions for this:

  1. Use a mocking framework (i.e. Mockito) to mock the datasource. This way you will only test your DAO logic
  2. Use a seperate database for testing that can be set up for every test. You can use DBUnit to do this.
  3. Use a in-memory database (i.e. HSQL) so changes won't persist.

This way you won't have to rollback your changes.

0

Please make sure you must need to declare:

<tx:annotation-driven transaction-manager="transactionManager" /> 

in your spring configuration file e.g. (applicationContext.xml).

As @Lieber already explained but more explanation is: MySQL Server (version 3.23-max and all versions 4.0 and above) supports transactions with the InnoDB transactional storage engine. InnoDB provides full ACID compliance. See Chapter 14, Storage Engines. For information about InnoDB differences from standard SQL with regard to treatment of transaction errors, see Section 14.3.13, “InnoDB Error Handling”.

The other nontransactional storage engines in MySQL Server (such as MyISAM) follow a different paradigm for data integrity called “atomic operations.” In transactional terms, MyISAM tables effectively always operate in autocommit = 1 mode. Atomic operations often offer comparable integrity with higher performance. So, MyISAM transactional storage engine always autocommit transaction.

Java SE
  • 2,073
  • 4
  • 19
  • 25
0

try to annotate the test method with @Transactional

Josef Prochazka
  • 1,273
  • 2
  • 9
  • 28
0

Haha had this same thing happen to me like 3 days ago. Try adding transactionManager ="transactionManager" to your @contextConfiguration:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:PersistenceHelper-context.xml"})
@TransactionConfiguration(defaultRollback = true, transactionManager = "transactionManager")
@Transactional

Also, I am not 100% percent sure on this, but your XML file should follow the "*-context" format. Let me know if you would like to see my PersistenceHelper-context.xml file.

nook
  • 2,378
  • 5
  • 34
  • 54
0
  • Try extending AbstractTransactionalJUnit4SpringContextTests
  • Note that MySql MYISAM tables are not transactional, you need to use INNODB type tables (see here and here)
  • Is your DAO also marked with @Transactional? This could cause a problem (see here)
Community
  • 1
  • 1
lucrussell
  • 5,032
  • 2
  • 33
  • 39
0

Try with following method after your test case logic.

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
Nirmal
  • 4,789
  • 13
  • 72
  • 114
  • this code throw exception:org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope – hudi Jun 20 '12 at 07:44
0

Your Dao needs to be marked @Transactional.

Also annotate your test with:

@TestExecutionListeners(TransactionalTestExecutionListener.class)  //Rolls back transactions by default
Daniel Alexiuc
  • 13,078
  • 9
  • 58
  • 73