9

I have some short unit tests that fail with the exception:

javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not prepare statement
::
Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement
::
Caused by: org.h2.jdbc.JdbcSQLException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-197]

The source is my Spring Data AuditProvider, specifically this line:

user = entityManager.createNamedQuery("findUserByUsernameAndTenant", User.class)
        .setParameter("tenant", TenantService.DEFAULT_TENANT_ID)
        .setParameter("username", UserService.USER_SYSTEM).getSingleResult();

The Error happens only when executing the whole test suite, not when running this test class only.

Here the TestRunner I'm using etc:

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@Rollback
public class MyTest {

That's my datasource URL:

spring.datasource.url: 'jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE'

So it seems "DB_CLOSE_ON_EXIT" doesn't solve the problem, any idea what's happening here?

UPDATE:

I just realized, that this happens only when running the tests in Eclipse, but they run through in the command line. While I do get the occasional:

o.s.b.f.support.DisposableBeanAdapter    : Invocation of destroy method failed on bean with name 'inMemoryDatabaseShutdownExecutor': org.h2.jdbc.JdbcSQLException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL)

but I don't get the PersistenceException and stacktrace.

Benjamin Maurer
  • 3,602
  • 5
  • 28
  • 49
  • Be aware the in-memory database is discarded, by default, when the connection closes. You can override this behavior. See: [*Keep H2 in-memory database between connections*](https://dba.stackexchange.com/q/224338/19079) and [*H2 in-memory database. Table not found*](https://stackoverflow.com/q/5763747/642706) – Basil Bourque Dec 06 '18 at 21:35

2 Answers2

15

Use DB_CLOSE_DELAY only.

For in-memory database, you shouldn't use DB_CLOSE_ON_EXIT=FALSE: you should only use DB_CLOSE_DELAY=-1. See http://www.h2database.com/html/features.html#in_memory_databases

So you datasource should be:

spring.datasource.url: 'jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1'

It is also possible that your unit tests are executed in parallel processes. Make sure that they all run in the same VM.

If you use Maven, set forkCount to 0:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.0</version>
  <configuration>
    <forkCount>0</forkCount>
  </configuration>
</plugin>
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • I tested it with your suggestions. I've changed the connection string and added the plugin config. It didn't change anything. I ran the tests in the commandline and they work, see my edit above. :/ (for clarification: they work with or without forkCount=0 and in Eclipse they always get marked failed for the exception) – Benjamin Maurer Sep 24 '18 at 16:13
  • @BenjaminMaurer Sorry for you. Unfortunately I have no clue as to why the integration in Eclipse is so different from the usual Maven build. This is mostly why I use either Netbeans or IntelliJ: their integration with Maven or Gradle is way better. – Olivier Grégoire Sep 25 '18 at 07:28
  • 1
    @OlivierGrégoire it is not true that you shouldn't use `DB_CLOSE_ON_EXIT=FALSE` see [the doc](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-embedded-database-support) - _If, for whatever reason, you do configure the connection URL for an embedded database, take care to ensure that the database’s automatic shutdown is disabled._ – MAREK Apr 18 '19 at 07:09
3

I guess your various unit test classes are all annotated with @RunWith(SpringJUnit4ClassRunner.class) ?

If so, every started test class will boot Spring which will in turn boot JPA. If multiple tests classes are run in parallel, each of them creating, then dropping, the SAME in-memory database, you might have some concurrency issues.

You may also have to set surefire reuseForks parameter to false in order to prevent JUnit from reusing the same 'context' for consecutive tests

TacheDeChoco
  • 3,683
  • 1
  • 14
  • 17