1

I use H2 database with Spring, Hibernate and DBUnit for testing. I also use c3p0 connection pool. One of my tests, however strangely fails immediately after it is passes successfully, some exception occurs after the test execution on rollback. My connection URL is jdbc:h2:file:h2/db;MODE=MySQL;LOCK_MODE=0 The following is the test itself.

@Test
public testFindLatestNotSynced() {
    BuyWine buyWine = buyWineDao.findLatestNotSynced(1).get(0);
    assertNotNull(buyWine);
    assertEquals("3", buyWine.getId());

    buyWine.setSyncDate(new Date());

    buyWine = buyWineDao.findLatestNotSynced(1).get(0);
    assertNotNull(buyWine);
    assertEquals("1", buyWine.getId());

    buyWine.setSyncDate(new Date());

    /* Checking empty. */
    List<BuyWine> wines = buyWineDao.findLatestNotSynced(1);
    assertEquals(0, wines.size());
}

The setup() and teardown methods are.

@Autowired
private IDatabaseConnection dbUnitConnection;

@Autowired
private FlatXmlDataSetBuilder datasetBuilder;

private ReplacementDataSet dataSet;

...

@Before
public void setUp() throws Exception {
    if (initialDatasetName != null) {
        /* Using replacement data set, so we can use null values in the first row. */
        dataSet = new ReplacementDataSet(
                datasetBuilder.build(getClass().getResourceAsStream(initialDatasetName)));
        dataSet.addReplacementObject("[NULL]", null);
        dataSet.setStrictReplacement(true);
        DatabaseOperation.CLEAN_INSERT.execute(dbUnitConnection, dataSet);
    }
}

@After
public void tearDown() throws Exception {
    if (dataSet != null) {
        DatabaseOperation.DELETE_ALL.execute(dbUnitConnection, dataSet);
    }
    dbUnitConnection.close();
    DataSourceUtils.releaseConnection(connection, dataSource);
}

It looks very much like a bug in H2, but I want to figure out some workaround for that.

The stack trace of the exception is:

org.springframework.transaction.TransactionSystemException: Could not roll back Hibernate transaction; nested exception is org.hibernate.TransactionException: rollback failed
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doRollback(HibernateTransactionManager.java:577)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:846)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:823)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:588)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:297)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:192)
    at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:395)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:91)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.hibernate.TransactionException: rollback failed
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:215)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doRollback(HibernateTransactionManager.java:574)
    ... 28 more
Caused by: org.hibernate.TransactionException: unable to rollback against JDBC connection
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:167)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:209)
    ... 29 more
Caused by: org.h2.jdbc.JdbcSQLException: General error: "java.lang.ArrayIndexOutOfBoundsException: 0"; SQL statement:
ROLLBACK [50000-174]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:332)
    at org.h2.message.DbException.get(DbException.java:161)
    at org.h2.message.DbException.convert(DbException.java:284)
    at org.h2.table.RegularTable.removeRow(RegularTable.java:394)
    at org.h2.engine.UndoLogRecord.undo(UndoLogRecord.java:98)
    at org.h2.engine.Session.rollbackTo(Session.java:586)
    at org.h2.engine.Session.rollback(Session.java:554)
    at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:50)
    at org.h2.command.CommandContainer.update(CommandContainer.java:79)
    at org.h2.command.Command.executeUpdate(Command.java:253)
    at org.h2.jdbc.JdbcConnection.rollbackInternal(JdbcConnection.java:1445)
    at org.h2.jdbc.JdbcConnection.rollback(JdbcConnection.java:470)
    at com.mchange.v2.c3p0.impl.NewProxyConnection.rollback(NewProxyConnection.java:860)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:163)
    ... 30 more
Caused by: java.lang.ArrayIndexOutOfBoundsException: 0
    at org.h2.index.PageBtree.getRow(PageBtree.java:172)
    at org.h2.index.PageBtreeLeaf.remove(PageBtreeLeaf.java:228)
    at org.h2.index.PageBtreeIndex.remove(PageBtreeIndex.java:239)
    at org.h2.table.RegularTable.removeRow(RegularTable.java:376)
    ... 40 more
Vic
  • 1,778
  • 3
  • 19
  • 37
  • You are using LOCK_MODE 0, meaning locking is disabled. This is [dangerous when using multiple connections](http://www.h2database.com/html/faq.html#reliable). I guess you are using multiple connections? – Thomas Mueller Dec 22 '13 at 16:42
  • @ThomasMueller I know, but I use H2 only for testing purposes, so disabled locking should not be a problem, I guess. Contrary, when I enable locking, I get a load or strange exceptions when DB unit starts to set up the DB. If you think, it can be relevant, I can post those exceptions as well. – Vic Dec 23 '13 at 08:47
  • Well, if you say disable locking, then it is dangerous and you could get such exceptions. You say you get exceptions when the database is set up when using locking, so do you use multiple connections concurrently to setup the database? That's rather unusual. I guess you should check your code. – Thomas Mueller Dec 23 '13 at 18:48
  • @ThomasMueller, can multiple connections appear because of c3p0 connection pool? I have no other ideas why that may happen. – Vic Jan 15 '14 at 09:34
  • I don't think it's a bug in c3p0. Most likely, the problem is your test case. I'm afraid I can't analyze it for you, sorry. All I can say is _don't_ disable locking. – Thomas Mueller Jan 15 '14 at 11:28
  • @ThomasMueller When I enable locking I get the following error in the `tearDown()` method for the tests, which modify the DB: `Timeout trying to lock table "WINE_GRAPES"; SQL statement: delete from wine_grapes` – Vic Jan 15 '14 at 14:33
  • OK, do you understand what the exception message means? If not, could you read the documentation? – Thomas Mueller Jan 15 '14 at 18:06
  • The message means the table is still locked by an open transaction. You should commit or rollback transactions within the test. – Thomas Mueller Jan 15 '14 at 18:42
  • @ThomasMueller thanks for the hint. After a pause I returned back to this problem. After coming across this question http://stackoverflow.com/questions/8591487/getting-dbunit-to-work-with-hibernate-transaction and this article http://www.nurkiewicz.com/2011/11/spring-pitfalls-transactional-tests.html I found, that most likely it happens because the changes are not committed and DbUnit has its own DB connection. When I change C3P0 to the default Hibernate pool it works fine. As for rolling back transaction manually, I'm reluctant to do that. In my code I've always tried to automate it. – Vic Apr 10 '14 at 08:06

0 Answers0