I try to set up a Junit test case for my dao layer. However, I do NOT want the test data to be actually persisted to the DB.
So I thought I should do it transactionally and rollback after every test. This leaves me with the following datasource setup:
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
p:driverClass="org.postgresql.Driver"
p:jdbcUrl="jdbc:postgresql://***"
p:user="***"
p:password="***/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="***"
p:hibernateProperties-ref="hibernateProps" />
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:dataSource-ref="dataSource"
p:sessionFactory-ref="sessionFactory"/>
The to-be-tested dao class is set up as follows:
@Repository
@Transactional
@SuppressWarnings("unchecked")
public class BallotBoxRepositoryHibernateImpl implements BallotBoxRepository {
@Autowired
private SessionFactory sessionFactory;
@Override
public void saveVote(DaoObject v) {
Session sess = sessionFactory.openSession();
sess.beginTransaction();
sess.save(v);
sess.getTransaction().commit();
sess.close();
}
[...]
}
The actual persisting job does work nicely. However, the intended Rollback is never made:
INFO main transaction.TransactionalTestExecutionListener:292 - Rolled back transaction after test execution for test context [...]
The TransactionalTextExecutionListener is defined as follows:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/spring/applicationContext.xml"})
@TestExecutionListeners({WebTestExecutionerListener.class, DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class})
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public class DaoTest { ... }
I'm using an autowired member to access my dao methods:
@Autowired
private BallotBoxRepository repo;
TL;DR
JUnit test case is persisting test data even though it's stating that a rollback has been done.
The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.
@Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.
I found that the @Transactional is used to ensure transaction on repository method or on a service method. @Lock is used on repository method to ensure locking of entity to provide isolation.
Add @Transactional(rollbackFor = Exception. class) to the top of the method. Spring will rollback all data within this transaction for you if any exception is thrown by database. Save this answer.
In your case Spring-based transaction management doesn't work because you manage Hibernate transactions manually.
In order to use Spring-managed transactions you should do the following:
@Override
public void saveVote(DaoObject v) {
sessionFactory.getCurrentSession().save(v);
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With