【问题标题】:Why a liquibase migration works on mysql and not in h2为什么 liquibase 迁移适用于 mysql 而不是 h2
【发布时间】:2015-06-12 17:23:04
【问题描述】:

我正在创建一个基于jhipster 的项目,但在集成测试中遇到了一些问题。我已经从 t_user 表中删除了登录列,并进行了一些更改以使用电子邮件作为登录名。 liquibase 迁移在 MySQL 中运行良好,但是当我运行一些测试时出现此错误:

java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:200)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:252)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:254)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:217)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is java.lang.RuntimeException: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(org.springframework.security.config.annotation.ObjectPostProcessor,java.util.List) throws java.lang.Exception; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService com.brevleq.consami.config.SecurityConfiguration.userDetailsService; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDetailsService': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.brevleq.consami.repository.UserRepository com.brevleq.consami.security.UserDetailsService.userRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot create inner bean '(inner bean)#1f481ec3' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#1f481ec3': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [com/brevleq/consami/config/DatabaseConfiguration.class]: Invocation of init method failed; nested exception is liquibase.exception.MigrationFailedException: Migration failed for change set classpath:config/liquibase/changelog/20150328154659_changelog.xml::1427568441617-1::hudson (generated):
 Reason: liquibase.exception.DatabaseException: org.h2.jdbc.JdbcSQLException: Constraint "IDX_USER_LOGIN" not found; SQL statement:
ALTER TABLE PUBLIC.t_user DROP CONSTRAINT idx_user_login [90057-183]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:474)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:98)
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:68)
at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:86)
... 28 more

如您所见,错误是找不到“idx_user_login”,因此它停止了数据库迁移中的测试。 指定的迁移文件是这样的:

<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd">
  <changeSet author="hudson (generated)" id="1427568441617-1">
    <dropUniqueConstraint constraintName="idx_user_login" tableName="t_user"/>
  </changeSet>
  <changeSet author="hudson (generated)" id="1427568441617-2">
    <dropUniqueConstraint constraintName="login" tableName="t_user"/>
  </changeSet>
  <changeSet author="hudson (generated)" id="1427568441617-3">
    <dropColumn columnName="login" tableName="t_user"/>
  </changeSet>
  <changeSet author="hudson (generated)" id="1427568441617-4">
    <addNotNullConstraint columnDataType="varchar(50)" columnName="email" tableName="T_USER"/>
  </changeSet>
</databaseChangeLog>

正如我所说,这个迁移在 MySQL 中运行正常。此文件是使用

生成的

mvn 编译 liquibase:diff

我可以做些什么来使这个迁移在我的测试中起作用?

【问题讨论】:

  • 您希望测试做什么?是否要测试迁移?
  • @flup JHipster 有一些集成测试,它们失败了,因为迁移失败了。

标签: java integration-testing h2 liquibase jhipster


【解决方案1】:

我在使用 mysql 进行生产和使用 H2 进行测试时遇到了同样的问题 通过禁用 liquibase 进行测试并启用休眠模式创建来解决它。 在 test/resources/application.properties 中

spring.datasource.url = jdbc:h2:mem:test;MODE=Mysql
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create #Replace validate !Here! 
#Liquibase
spring.liquibase.enabled=false #Replace true by false !Here!

【讨论】:

    【解决方案2】:

    我也遇到了同样的问题,但事实证明 H2 仅用作某些测试的测试数据库,这些测试确实需要配置 Spring 上下文(此类测试在启用“测试”配置文件的情况下运行)。在我们的案例中,解决方案是简单地删除 data.* 文件(H2 的数据文件),因为它们与传入的更改处于不一致的状态。

    如果您的案例是本地开发数据库,​​那么丢失数据不是什么大问题,这个解决方案可能对您有用。

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题:删除 PostgreSQL 中的唯一约束工作正常,当我对 H2 数据库运行集成测试时,它抱怨找不到我想要删除的约束。

      我发现,在创建约束时,没有配置特定的 constraintName。 显然 PostgreSQL 和 H2 没有生成相同的约束名称。这就是为什么它只在其中一个中起作用的原因。

      我的解决方案是通过添加特定名称来修复先前创建约束的脚本。

      <column name="report_id" type="bigint">
          <constraints unique="true" uniqueConstraintName="visit_report_id_key"/>
      </column>
      

      这样,PostgreSQL和H2没有区别

      【讨论】:

        【解决方案4】:

        这是我的解决方案,如何在 H2 上执行文本之前运行 liquibase:

        只需将src/main/resources/liquibase/master.xml 复制到src/test/resources/liquibase/master.xml

        在 src/test 下制作 master.xml 的副本后,liquibase 也会神奇地在测试之前运行并应用所有变更集。因为 H2 测试数据库通常不是持久化的,所以 liquibase 每次都会在干净的数据库上传播其变更集。

        但是,您必须在每次创建变更集时复制 master.xml。我没有找到任何方法(直到现在)如何设置 JHipster 以自动将变更集应用到测试数据库。

        【讨论】:

        • 这个答案对我有帮助。但是,我没有手动复制它,而是在maven-surefire-plugin 的执行配置中添加了&lt;include&gt;src/main/resources/liquibase/master.xml&lt;/include&gt;。这也解决了问题。
        猜你喜欢
        • 1970-01-01
        • 2021-09-05
        • 2018-10-12
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 2017-06-12
        相关资源
        最近更新 更多