单元测试中的jOOQ + Liquibase + H2导致"未找到模式"例外

时间:2018-02-15 16:08:24

标签: java sql h2 liquibase jooq

我正在为某些数据访问代码编写单元测试。设置中的关键部分包括:

  • jOOQ为CRUD操作生成工件
  • Liquibase处理架构演变

尽可能多的,我试图按如下方式设置测试:

  1. 创建java.sql.Connection以使用适当命名的架构初始化H2数据库。 (这里值得注意的是,使用以下URL创建连接: jdbc:h2:mem:[schema-name];MODE=MySQL;DB_CLOSE_DELAY=-1)。

  2. 使用上述连接,调用Liquibase来运行创建数据库模式中所有对象的更改日志

  3. 使用上述连接,创建一个org.jooq.DSLContext,用于测试数据访问组件。

  4. 抽象类将这三个步骤封装在@Before带注释的方法中,测试类扩展此抽象类以利用初始化的org.jooq.DSLContext实例。像这样的东西:

    abstract class DbTestBase {
        protected lateinit var dslContext: DSLContext
    
        private lateinit var connection: Connection
    
        open fun setUp() {
            connection = DriverManager.getConnection("jdbc:h2:mem:foo;MODE=MySQL;DB_CLOSE_DELAY=-1")
    
            // invoke Liquibase with this connection instance...
    
            dslContext = DSL.using(connection, SQLDialect.H2)
        }
    
        open fun tearDown() {
            dslContext.close()
    
            connection.close()
        }
    }
    
    class MyTest : DbTestBase() {
        private lateinit var repository: Repository
    
        @Before override fun setUp() {
            super.setUp()
    
            repository = Repository(dslContext)
        }
    
        @After override fun tearDown() {
            super.tearDown()
        }
    
        @Test fun something() {
            repository.add(Bar())
        }
    }
    

    这导致以下异常:

    Caused by: org.h2.jdbc.JdbcSQLException: Schema "foo" not found; SQL statement:
    insert into `foo`.`bar` (`id`) values (?) [90079-196]
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
        at org.h2.message.DbException.get(DbException.java:179)
        at org.h2.message.DbException.get(DbException.java:155)
        at org.h2.command.Parser.getSchema(Parser.java:688)
        at org.h2.command.Parser.getSchema(Parser.java:694)
        at org.h2.command.Parser.readTableOrView(Parser.java:5535)
        at org.h2.command.Parser.readTableOrView(Parser.java:5529)
        at org.h2.command.Parser.parseInsert(Parser.java:1062)
        at org.h2.command.Parser.parsePrepared(Parser.java:417)
        at org.h2.command.Parser.parse(Parser.java:321)
        at org.h2.command.Parser.parse(Parser.java:293)
        at org.h2.command.Parser.prepareCommand(Parser.java:258)
        at org.h2.engine.Session.prepareLocal(Session.java:578)
        at org.h2.engine.Session.prepareCommand(Session.java:519)
        at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1204)
        at org.h2.jdbc.JdbcPreparedStatement.<init>(JdbcPreparedStatement.java:73)
        at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:288)
        at org.jooq.impl.ProviderEnabledConnection.prepareStatement(ProviderEnabledConnection.java:106)
        at org.jooq.impl.SettingsEnabledConnection.prepareStatement(SettingsEnabledConnection.java:70)
        at org.jooq.impl.AbstractQuery.prepare(AbstractQuery.java:410)
        at org.jooq.impl.AbstractDMLQuery.prepare(AbstractDMLQuery.java:342)
        at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:316)
        ... 25 more
    

    我可以在重新生成架构的位置看到Liquibase日志记录。我已经改变了H2 URL来创建基于文件的数据库,我能够检查并验证模式确实存在。

    我很感激任何帮助,发现方法中的任何错误。

1 个答案:

答案 0 :(得分:2)

经过多次试验和错误,我能够通过解决两个问题解决我的问题:

  1. Lukas评论区分数据库&#34; (或&#34;目录&#34;)vs&#34;架构&#34;把我推向正确的方向。尽管这些术语似乎可以在MySQL(我的生产数据库)中互换使用,但它们并不在H2中。事后似乎相当明显,但对此的补救措施是调用JDBC调用来手动构建模式,然后在调用Liquibase重构模式之前将其设置为默认模式,la:

    ...
    connection = DriverManager.getConnection(...)
    
    connection.createStatement().executeUpdate("create schema $schemaName")
    
    connection.schema = schemaName.toUpperCase()
    
    // invoke Liquibase with this connection
    ...
    
  2. 尽管打开了与H2不区分大小写的连接,但toUpperCase()调用仍然是必要的。不知道为什么......

    1. jOOQ引用所有架构对象的名称,以使它们不区分大小写。但是,正如我所了解的那样,引用用于在H2中强制执行区分大小写。因此,生成的查询中存在引号会导致由于无法找到的对象而导致的大量错误。解决这个问题的方法是为查询生成器提供一个不同的RenderNameStyle来省略引号,la:

      ...
      val settings = Settings().withRenderNameStyle(RenderNameStyle.AS_IS)
      
      val dslContext = DSL.using(connection, SQLDialect.H2, settings)
      ...
      
    2. 希望这对其他人有所帮助。