-->

Getting DbUnit to Work with Hibernate Transaction

2019-04-08 03:58发布

问题:

I'm having problem trying to push changes made within a Hibernate transaction to the database for DbUnit to work properly in my test case. It seems like DbUnit is not seeing the changes made by Hibernate because they are not committed at the end of the transaction yet... and I'm not sure how to restructure my test case to get this to work.

Here's my over-simplified test case to demonstrate my problem:-

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:applicationContext-test.xml"
})
@TransactionConfiguration(transactionManager = "transactionManager")
@Transactional
public class SomeTest {
    @Autowired
    protected DataSource dataSource;

    @Autowired
    private SessionFactory sessionFactory;

    @Test
    public void testThis() throws Exception {
        Session session = sessionFactory.getCurrentSession();

        assertEquals("initial overlayType count", 4, session.createQuery("from OverlayType").list().size());

        //-----------
        // Imagine this block is an API call, ex: someService.save("AAA");
        // But for the sake of simplicity, I do it this way
        OverlayType overlayType = new OverlayType();
        overlayType.setName("AAA");
        session.save(overlayType);
        //-----------

        // flush has no effect here
        session.flush();

        assertEquals("new overlayType count", 5, session.createQuery("from OverlayType").list().size());

        // pull the data from database using dbunit
        IDatabaseConnection connection = new DatabaseConnection(dataSource.getConnection());
        connection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new MySqlDataTypeFactory());
        QueryDataSet partialDataSet = new QueryDataSet(connection);
        partialDataSet.addTable("resultSet", "select * from overlayType");
        ITable actualTable = partialDataSet.getTable("resultSet");

        // FAIL: Actual row count is 4 instead of 5
        assertEquals("dbunit's overlayType count", 5, actualTable.getRowCount());

        DataSourceUtils.releaseConnection(connection.getConnection(), dataSource);
    }
}

My whole idea in using DbUnit is to:-

  • Call someService.save(...) that saves data into several tables.
  • Use DbUnit to get expected table from XML.
  • Use DbUnit to get actual table from database.
  • Do Assertion.assertEquals(expectedTable, actualTable);.

But, at this point, I'm not able to get DbUnit to see the changes made by Hibernate within the transaction.

How should I change to get DbUnit to work nicely with Hibernate transaction?

Thanks.

回答1:

I have never worked with DbUnit, but it seems like TransactionAwareDataSourceProxy will do the trick. Basically you need to wrap your original data source with this proxy and use it instead, so that this code:

new DatabaseConnection(dataSource.getConnection())

actually goes through the proxy and uses the same transaction and connection as Hibernate.

I found Transaction aware datasource (use dbunit & hibernate in spring) blog post explaining this.

Another approach would be to skip transactional tests altogether and cleanup the database instead manually. Check out my transactional tests considered harmful artcle.



回答2:

Looks like that test case needs two transactions: one for putting data into the database, and a second one to retrieve it.

What I would do is:

  • Use a memory database so the data is cleaned when the unit test ends.
  • Remove the transactional annotations and use the beginTransaction and commit methods of the session directly.

The initial overlaytype count would be 0, and after the session is saved, it should be 1.