为什么 liquibase 迁移适用于 mysql 而不是 h2

Why a liquibase migration works on mysql and not in h2

我正在创建一个基于 jhipster 的项目,但我在集成测试中遇到了一些问题。我已经从 t_user table 中删除了登录列,并做了一些更改以使用电子邮件作为登录名。 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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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 compile liquibase:diff

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

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

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

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

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

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

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

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

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

这样一来PostgreSQL和H2就没有区别了

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

如果您的情况是针对本地开发数据库,​​那么丢失数据不是什么大问题,此解决方案可能适合您。

我在使用 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!