Transaction aware datasource (use dbunit & hibernate in spring)

April 27, 2008 at 5:03 pm | Posted in Uncategorized | 3 Comments
Tags: , ,

[UseCase]

use dbunit & hibernate in unit test

  • prepare test data with dbunit using datasource
  • test a dao method with hibernate
  • rollback all data (each testcase doesn’t pollute data)

[Issue]

preparing test data and running a test method must be executed under the same transaction in order to rollback.
=> dbunit(datasource) needs to participate spring transaction management.

[Solution]

use TransactionAwareDatasourceProxy

[How it works]

Transaction manager acquires a DB connection and put it to connection holder in thread local.
Whenever getConnection() is called, same connection will be retrieved from connection holder.
When close() is called, it doesn’t close connection.
At the end of transaction, finally connection will be closed.

ConnectionHolder
When transaction manager begins transaction, ConnectionHolder instance will be created and hold actual connection.
When transaction is done, connection will be closed.

TransactionAwareDataSourceProxy
wrap original datasource.
implementing DataSource interface
participate spring managed transaction control

TransactionAwareInvocationHandler
proxy of Connection class
private inner class
delegate standard Connection methods(equals, close, …) to DataSourceUtils

[Sample Config]

<!--
 | Datasource with Transaction support.
-->

<bean id="dataSource" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
     <constructor-arg ref="dbcpDataSource"/>
</bean>

<bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
     <property name="driverClassName" value="${hibernate.connection.driver_class}"/>
     <property name="url" value="${hibernate.connection.url}"/>
     <property name="username" value="${hibernate.connection.username}"/>
     <property name="password" value="${hibernate.connection.password}"/>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:hibernate.cfg.xml"/>
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration"/>
    <property name="hibernateProperties" ref="hibernateProperties"/>
    <!--<property name="useTransactionAwareDataSource" value="true"/>-->
</bean>

[Sample Test Code]

final IDatabaseConnection dbUnitConnection = new DatabaseConnection(this.getDataSource());

// Do some operations with dbunit
// eg)
// DatabaseOperation.DELETE.execute(connection, dataSet);

// this doesn't close connection since method is executed under transaction
DataSourceUtils.releaseConnection(dbUnitConnection.getConnection(), this.getDataSource());

[Key Spring Classes]

About these ads

3 Comments »

RSS feed for comments on this post. TrackBack URI

  1. […] TransactionAwareDataSourceProxy Let your datasource participate spring transaction management. reference my other post. […]

  2. Hi,

    Another very simple way to run DBUnit injection from within a Spring transaction is to use Spring’s DataSourceUtils class. In few words, here is my context:

    1) My test classes are annotated with:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "/WEB-INF/spring-test-config.xml" })
    @Transactional(propagation=Propagation.REQUIRED)

    Thus, a transaction will be created as soon as the test is run.

    2) Then, inside the @Before method of my test class, I’m initializing DB data using DBUnit. I provide to DBUnit a connection instance get from the Spring’s DataSourceUtils, which is transaction-aware. Here is how:

    IDatabaseConnection connection = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
    IDataSet xmlFileDataSet = new FlatXmlDataSetBuilder().build(........);
    DatabaseOperation.INSERT.execute(connection, finalDataSet);

    3) All my classes under test are annotated with @Transactional(propagation=Propagation.REQUIRED) or @Transactional(propagation = Propagation.MANDATORY), so they will be injected with the transaction initially created in the test class.

    And that’s all :) Spring knowing that we are running in test context, it automatically rolls back the transaction at the end of every test. It also hopefully rolls back the transaction in case of test failure.

    The goal of all of this is to avoid FK violations by DBUnit when trying to delete data in a wrong order. That way, the database is always “empty” and the only thing you have to do is write your flat XML files correctly so that data insertion will not break FKs.

    Hope this may be of some help :)

    Regards,
    Avangel

    • great thanks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com. | The Pool Theme.
Entries and comments feeds.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: