Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Schema related problems with Flyway / Spring and H2 embedded database

Tags:

spring

h2

flyway

I am building a Spring 3 MVC app that uses a MySQL database and have recently integrated Flyway into the solution to manage the database migrations. I have successfully configured my applicationContext.xml according to the Flyway documentation such that, upon application startup, Flyway will migrate to the latest version.

I am having trouble getting Flyway to play nicely with my unit / functional tests. I am using Spring Data JPA for my data access layer and have built some JUnit tests to test some custom queries.

The application config I use for these tests is:

<jdbc:embedded-database id="dataSource" type="H2" />

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
  <property name="driverClassName" value="org.h2.Driver"/>
  <property name="url" value="jdbc:h2:mem:medical_claims_tracker;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;MODE=MySQL;INIT=CREATE SCHEMA IF NOT EXISTS medical_claims_tracker" />
</bean>

<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">
    <property name="dataSource" ref="dataSource"/>
    <property name="schemas" value="medical_claims_tracker"/>
    <property name="sqlMigrationPrefix" value="Migration_"/>
</bean>

When I run my unit tests (either through Eclipse or using Maven) I get the following exception:

ERROR: org.springframework.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@4cd4544] to prepare test instance [name.hines.steven.medical_claims_tracker.repositories.ExpenseRepositoryTest@1664a9b]
java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:103)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:73)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
    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:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    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:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flyway' defined in class path resource [tests_persistence-applicationContext.xml]: Invocation of init method failed; nested exception is com.googlecode.flyway.core.api.FlywayException: Error setting current schema to medical_claims_tracker
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:532)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:741)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flyway' defined in class path resource [tests_persistence-applicationContext.xml]: Invocation of init method failed; nested exception is com.googlecode.flyway.core.api.FlywayException: Error setting current schema to medical_claims_tracker
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1486)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:285)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:439)
    at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:277)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.<init>(PersistenceExceptionTranslationInterceptor.java:79)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor.<init>(PersistenceExceptionTranslationAdvisor.java:71)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.setBeanFactory(PersistenceExceptionTranslationPostProcessor.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1506)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1474)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
    ... 37 more
Caused by: com.googlecode.flyway.core.api.FlywayException: Error setting current schema to medical_claims_tracker
    at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1250)
    at com.googlecode.flyway.core.Flyway.migrate(Flyway.java:820)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483)
    ... 54 more
Caused by: org.h2.jdbc.JdbcSQLException: Schema "medical_claims_tracker" not found; SQL statement:
SET SCHEMA "medical_claims_tracker" [90079-160]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:169)
    at org.h2.message.DbException.get(DbException.java:146)
    at org.h2.engine.Database.getSchema(Database.java:1501)
    at org.h2.command.dml.Set.update(Set.java:290)
    at org.h2.command.CommandContainer.update(CommandContainer.java:73)
    at org.h2.command.Command.executeUpdate(Command.java:219)
    at org.h2.jdbc.JdbcPreparedStatement.execute(JdbcPreparedStatement.java:181)
    at com.googlecode.flyway.core.dbsupport.JdbcTemplate.execute(JdbcTemplate.java:280)
    at com.googlecode.flyway.core.dbsupport.h2.H2DbSupport.setCurrentSchema(H2DbSupport.java:79)
    at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1247)
    ... 62 more

I can't work out how to get the H2 database to recognise the fact that I want to use a schema called medical_claims_tracker. I used this answer to help me try out different connection URLs and this answer to help me nail down the applicationContext overrides for use during testing.

I suspect it is something to do with H2 dropping the schema after initial creation but before the test can be run, but I thought that DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; would solve that for me. Interestingly, if I remove the specification of the schema in the flyway bean config:

<bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate">
    <property name="dataSource" ref="dataSource"/>
    <property name="sqlMigrationPrefix" value="Migration_"/>
</bean>

I get a different exception:

INFO : com.googlecode.flyway.core.metadatatable.MetaDataTableImpl - Creating Metadata table: "public"."schema_version"
ERROR: org.springframework.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener@4bd27069] to prepare test instance [name.hines.steven.medical_claims_tracker.repositories.ExpenseRepositoryTest@64d22462]
java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:103)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:73)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:313)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:284)
    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:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    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:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flyway' defined in class path resource [tests_persistence-applicationContext.xml]: Invocation of init method failed; nested exception is com.googlecode.flyway.core.api.FlywayException: Error executing statement at line 17: CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(30) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:532)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:741)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
    at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flyway' defined in class path resource [tests_persistence-applicationContext.xml]: Invocation of init method failed; nested exception is com.googlecode.flyway.core.api.FlywayException: Error executing statement at line 17: CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(30) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1486)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:285)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:439)
    at org.springframework.beans.factory.BeanFactoryUtils.beansOfTypeIncludingAncestors(BeanFactoryUtils.java:277)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:139)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.<init>(PersistenceExceptionTranslationInterceptor.java:79)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor.<init>(PersistenceExceptionTranslationAdvisor.java:71)
    at org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.setBeanFactory(PersistenceExceptionTranslationPostProcessor.java:85)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1506)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1474)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)
    ... 37 more
Caused by: com.googlecode.flyway.core.api.FlywayException: Error executing statement at line 17: CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(30) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
)
    at com.googlecode.flyway.core.dbsupport.SqlStatement.execute(SqlStatement.java:78)
    at com.googlecode.flyway.core.dbsupport.SqlScript.execute(SqlScript.java:94)
    at com.googlecode.flyway.core.metadatatable.MetaDataTableImpl$1.doInTransaction(MetaDataTableImpl.java:124)
    at com.googlecode.flyway.core.metadatatable.MetaDataTableImpl$1.doInTransaction(MetaDataTableImpl.java:121)
    at com.googlecode.flyway.core.util.jdbc.TransactionTemplate.execute(TransactionTemplate.java:54)
    at com.googlecode.flyway.core.metadatatable.MetaDataTableImpl.create(MetaDataTableImpl.java:121)
    at com.googlecode.flyway.core.metadatatable.MetaDataTableImpl.createIfNotExists(MetaDataTableImpl.java:134)
    at com.googlecode.flyway.core.Flyway$1.execute(Flyway.java:834)
    at com.googlecode.flyway.core.Flyway$1.execute(Flyway.java:820)
    at com.googlecode.flyway.core.Flyway.execute(Flyway.java:1259)
    at com.googlecode.flyway.core.Flyway.migrate(Flyway.java:820)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1612)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483)
    ... 54 more
Caused by: org.h2.jdbc.JdbcSQLException: Schema "public" not found; SQL statement:
CREATE TABLE "public"."schema_version" (
    "version_rank" INT NOT NULL,
    "installed_rank" INT NOT NULL,
    "version" VARCHAR(50) NOT NULL,
    "description" VARCHAR(200) NOT NULL,
    "type" VARCHAR(20) NOT NULL,
    "script" VARCHAR(1000) NOT NULL,
    "checksum" INT,
    "installed_by" VARCHAR(30) NOT NULL,
    "installed_on" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    "execution_time" INT NOT NULL,
    "success" BOOLEAN NOT NULL
) [90079-160]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
    at org.h2.message.DbException.get(DbException.java:169)
    at org.h2.message.DbException.get(DbException.java:146)
    at org.h2.command.Parser.getSchema(Parser.java:613)
    at org.h2.command.Parser.getSchema(Parser.java:620)
    at org.h2.command.Parser.parseCreateTable(Parser.java:5147)
    at org.h2.command.Parser.parseCreate(Parser.java:3800)
    at org.h2.command.Parser.parsePrepared(Parser.java:324)
    at org.h2.command.Parser.parse(Parser.java:279)
    at org.h2.command.Parser.parse(Parser.java:251)
    at org.h2.command.Parser.prepareCommand(Parser.java:217)
    at org.h2.engine.Session.prepareLocal(Session.java:415)
    at org.h2.engine.Session.prepareCommand(Session.java:364)
    at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1119)
    at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:164)
    at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:152)
    at com.googlecode.flyway.core.dbsupport.JdbcTemplate.executeStatement(JdbcTemplate.java:296)
    at com.googlecode.flyway.core.dbsupport.SqlStatement.execute(SqlStatement.java:76)
    ... 71 more

So it seems that with the first configuration, Flyway is able to create the schema_version table in the medical_claims_tracker schema but can't then find that schema to run the tests in, whereas in the second version, it can't even find the "default" schema in which to create the schema_version table. Is this correct, or have I got this the wrong way round?

like image 564
stevenghines Avatar asked Feb 20 '13 04:02

stevenghines


People also ask

Does Flyway create schema?

Schema creationBy default, Flyway will attempt to create the schemas provided by the schemas and defaultSchema configuration options. This behavior can be toggled with the createSchemas configuration option. This might be useful when you want complete control over how schemas are created.

What is schema in Flyway?

Flyway uses a schema history table to track the version of each database, recording in it every versioned migration file applied to build that version.

What is default schema in Flyway?

The default schema managed by Flyway. This schema will be the one containing the schema history table. If not specified in schemas, Flyway will automatically attempt to create and clean this schema first. This schema will also be the default for the database connection (provided the database supports this concept).


4 Answers

From http://www.h2database.com/javadoc/org/h2/engine/DbSettings.html:

databaseToUpper: Database setting DATABASE_TO_UPPER (default: true).

Database short names are converted to uppercase for the DATABASE() function ... Setting this to "false" is experimental. When set to false, all identifier names (table names, column names) are case sensitive...

the problem is that by default schema names are in UPPERCASE.

Solution:

add ;DATABASE_TO_UPPER=false to database url

like image 168
Omid Avatar answered Oct 27 '22 09:10

Omid


I had a similar issue, although I'm not using Spring. I'm adding H2 in-memory DB for tests on top of an existing setup with jOOQ + Flyway + PostgreSQL.

The solution for me was a combination of changes:

  1. From @Omid's answer above, add ;DATABASE_TO_UPPER=false to the startup URL
  2. From @Tom's answer, add the schema creation to an init SQL script: CREATE SCHEMA IF NOT EXISTS public; (I didn't need to use the backticks).
  3. Finally in the same script, add a 2nd line of SET SCHEMA public; after schema creation.

Note that I wasn't able to add ;SCHEMA=public to the init URL, because this was throwing an error before the schema was being created. Alternatively:

  1. Add ;DATABASE_TO_UPPER=false;INIT=CREATE SCHEMA IF NOT EXISTS public; to the startup URL.
  2. Then add SET SCHEMA public; to your init SQL script.
like image 33
Rónán Ó Braonáin Avatar answered Oct 27 '22 07:10

Rónán Ó Braonáin


The schema name in the init script should be quoted or the one you pass to Flyway put in uppercase.

like image 42
Axel Fontaine Avatar answered Oct 27 '22 07:10

Axel Fontaine


From what I've tested the whole problem occurs only while mixing flyway and H2 with MySql compatibility mode on (doesn't occur if MySql mode is of). It is caused by case sensitivity inconsistency:

  1. h2 during initialization creates default schema 'PUBLIC' (uppercase!)
  2. when flyway tries to get default schema name it gets 'public' (lowercase!)
  3. when flyway tries to change current schema to 'public' (lowercase!) error is thrown. Even when setting flyway schemas to 'MYSCHEMA', at some point of it migration process it always tries to change to 'public'.

In my currently developed application we use MySql as a main database and H2 for testing. My workaround involves creating 'public' (lowercase!) schema during h2 initialization:

spring datasource configuration:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
 <property name="driverClassName" value="org.h2.Driver"/>
 <property name="url" value="jdbc:h2:mem:MY_APP;MODE=MySQL;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM 'classpath:db/migration/test/init_tests.sql';"/>
 <property name="username" value="sa"/>
 <property name="password" value=""/>
</bean>

init_tests.sql:

CREATE SCHEMA IF NOT EXISTS "public";

Isn't beautifull but works - hope that helps!

like image 26
Tom Avatar answered Oct 27 '22 07:10

Tom