views:

22

answers:

1

All, I'm attempting to write a unit test for a Dao that uses Hibernate. I'm also using Spring, so I'm trying to extend AbstractTransactionalDataSourceSpringContextTests and also use DBUnit to insert data into the database before each test in the onSetUpInTransaction method.

From my logs, I can see that DbUnit is able to successfully insert the data in onSetUpInTransaction just fine. However, when I run a test method that uses the Dao (and therefore, Hibernate) to try to access that data (testGetPersonById2), the data isn't found, even though all this should be happening in the same transaction. After the test method finishes running (it fails), I see the log statement from the AbstractTransactionalDataSourceSpringContextTests that the transaction does get rolled back correctly.

It seems like the onSetUpInTransaction and Hibernate session must be using different transactions, but I can't figure out why. Does anyone have an example of something like this working? Advice on what I'm missing?

Here's what I've got so far:

public class PersonDaoTest extends AbstractTransactionalDataSourceSpringContextTests{

    private Log logger = LogFactory.getLog(PersonDaoTest.class);

    private PersonDaoImpl personDao;

    @Override
    public void onSetUpInTransaction() throws Exception {
    // Load test data using DBUnit
    super.onSetUpBeforeTransaction();
        DataSource ds = jdbcTemplate.getDataSource()
    Connection con = DataSourceUtils.getConnection(ds);
    IDatabaseConnection dbUnitCon = new DatabaseConnection(con);
    DatabaseConfig config = dbUnitCon.getConfig();
    config.setFeature("http://www.dbunit.org/features/qualifiedTableNames",
        true);

        //This dataset contains a single entry in the Persons table,
        // a new person with Id = 998877665, it gets inserted successfully
    IDataSet dataSet = new FlatXmlDataSet(new FileInputStream(
        "./PersonDaoTest.xml"));
    logger.warn("dataSet = " + dataSet);
    try {
        DatabaseOperation.REFRESH.execute(dbUnitCon, dataSet);
        SessionFactoryUtils.getSession(getSessionFactory(), false).flush();

    } finally {
        DataSourceUtils.releaseConnection(con, ds);
    }

    }

     //This test PASSES, because the Person with Id = 9 already
     //exists in the database, it does not rely on the data being set up in the        
     // onSetUpInTransaction method 
     @Test
     public void testGetPersonById() {

     Person person = personDao.findById(9L);
     assertNotNull("person should not be null", person);

     }

    //This test FAILS at the assertNotNull line, because
    //no Person with Id = 998877665 exists in the database,
    //even though that Person was inserted 
    //in the onSetUpInTransaction  method - it seems
    //that hibernate cannot see that insertion.
    @Test
    public void testGetPersonById2() {

    Person person = personDao.findById(998877665L);
    assertNotNull("person should not be null", person);

    }

UPDATE: Here's my spring config:

<bean id="propertyConfigurer"       class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

<bean id="dataSource" class="com.p6spy.engine.spy.P6DataSource">
  <constructor-arg>
    <bean id="basicDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
   <property name="driverClassName">
     <value>${jdbc.driverClassName}</value>
    </property>
    <property name="url">
       <value>${jdbc.url}</value>
    </property>
    <property name="username">
       <value>${jdbc.username}</value>
    </property>
     <property name="password">
       <value>${jdbc.password}</value>
      </property>
   </bean>
  </constructor-arg>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>


<!-- Hibernate SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource"><ref bean="dataSource"/></property>

        <property name="configLocation">
             <value>classpath:hibernate.cfg.xml</value>
        </property>

        <property  name="configurationClass">
            <value>org.hibernate.cfg.AnnotationConfiguration</value>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
                <prop key="hibernate.cache.use_query_cache">false</prop>
                <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
                <prop key="hibernate.cache.query_cache_factory">org.hibernate.cache.StandardQueryCacheFactory</prop>
            </props>
        </property>
    </bean>

    <!-- The Hibernate interceptor 
    <bean id="hibernateInterceptor" class="org.springframework.orm.hibernate3.HibernateInterceptor">
        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
    </bean>-->

     <bean id="personDao" class="my.dao.PersonDaoImpl">
        <property name="sessionFactory"><ref bean="sessionFactory"/></property>         
    </bean> 
A: 

I spent some more time with this, and never was able to find anything that worked when trying to use the onSetUpInTransaction method. I ended up switching over to using onSetUpBeforeTransaction and onTearDownAfterTransaction instead. It's not exactly ideal, because the onSetUpBeforeTransaction method does end up committing its data insertions to the database, and that data must then be cleaned up in onTearDownAfterTransaction. However, the tests themselves can still insert and update data all they want and have those changes all rolled back, since each test does still operate in its own transaction. So, I don't have to do anything special to clean up when a test inserts new data, which means I met one of my goals anyway!

elduff