IntegrationTest 隔离在 springboot 2.2.2.RELEASE 中失败(每个 SpringBootTest 之后的错误掺杂表)

Mar*_*cha 8 hibernate h2 spring-boot-test

我们的应用程序在 2.0.4 版本中运行。升级到 2.2.2.RELEASE 后,我们看到集成测试失败。我怀疑存在一些错误配置,并且每个集成测试在其自身之后都没有清理干净,或者存在之前不存在的额外初始化。我真的不知道如何正确修复它。

再具体一点。每个测试在单独调用时都有效。当执行所有这些时,我们确实看到如下错误:

org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "drop table somewhere.sometable if exists" via JDBC Statement
...
caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Cannot drop "SOME_TABLE" because "FKKJEJC7GUX6OTX5NGANQCMN83R, FK7WLRCFA21PY7CI3R4OL1OWODT, FKQPMY4YOVD3D6HBNT0XX92149P, FK1TG6AMM2NSM6UJTO9EJHPJIXY, FKLPTBKDKFCHE72RJ5RRRIH4ORJ" depends on it; SQL statement:
Run Code Online (Sandbox Code Playgroud)

2019-12-16 21:11:00.075 org.apache.tomcat.util.modeler.Registry  : The MBean registry cannot be disabled because it has already been initialised
Run Code Online (Sandbox Code Playgroud)

这暗示我,我们正在尝试重新初始化已经初始化的东西+休眠初始化中的删除顺序错误。但我真的看不出我们这边有什么问题。让我们展示一些摘录:

测试注释:

@RunWith(SpringRunner.class)
@ActiveProfiles(...)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SomeIT {
Run Code Online (Sandbox Code Playgroud)

测试通过以下方式执行:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.22.2</version>
  <configuration>
    <useSystemClassLoader>false</useSystemClassLoader>
    <forkCount>0</forkCount>
    <reuseForks>false</reuseForks>
  </configuration>
  <executions>
    <execution>
      <id>integration-test</id>
      <goals>
        <goal>integration-test</goal>
      </goals>
    </execution>
    <execution>
      <id>verify</id>
      <goals>
        <goal>verify</goal>
      </goals>
    </execution>
  </executions>
</plugin>
Run Code Online (Sandbox Code Playgroud)

和 application.properties 用于测试:

spring.jpa.database=H2
spring.jpa.hibernate.ddl-auto=create
spring.jpa.properties.hibernate.jdbc.batch_size=5
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true

#this disables option to have opened tx in view IIUC. We don't rely on that, so this just removes warning logging from console.
spring.jpa.open-in-view=false

#used to select db initialization scripts.
spring.datasource.platform=org.hibernate.dialect.H2Dialect

spring.datasource.url=jdbc:h2:mem:somewhere;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;INIT=create schema if not exists somewhere
spring.datasource.driver-class-name = org.h2.Driver

#this is probably needed for @DataJpaTest: I have no idea how to configure @DataJpaTest so that it can run with
#autoconfigured H2 db, probably it's caused by having schema defined in entities. Anyways @DataJpaTest fails to
#create schema. So alternative is to configure one DB for whole app here, and this option forces all @DataJpaTest not to
#replace this configuration with autoconfigured db.
spring.test.database.replace=none
Run Code Online (Sandbox Code Playgroud)

测试更改:

  • 如果有任何帮助,我将 create 更改为 create-drop,不,它没有任何帮助。
  • 我尝试在每个 IT 测试的类级别上使用 @DirtiesContext,无论如何,这是我所期望的,每个 IT 测试类都会创建/终止该上下文,但这也无济于事。
  • 我试图删除replace=none,但这只会杀死所有单元测试(因为未创建架构),并且对集成测试没有任何帮助。

当前的解决方法:好吧,我能想到的就是不要重用 db. 与replace=none只可能通过:

spring.datasource.url=jdbc:h2:mem:somewhere${random.uuid};DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;INIT=create schema if not exists somewhere
Run Code Online (Sandbox Code Playgroud)

但我非常讨厌这种“解决方案”。什么会导致不正确的数据库重新初始化/丢失清理/或者这一切的原因是什么?

(编辑:如果您知道这个问题的更好标题,请提出建议。谢谢)。

gti*_*333 9

从 2.2.0 升级到 Spring Boot 2.2.1 后,我遇到了同样的错误。看起来 2.2.1 已将 h2 依赖项更新为 1.4.200。( https://github.com/spring-projects/spring-boot/releases/tag/v2.2.1.RELEASE )

H2 v1.4.200 进行了更改以更新其 DROP TABLE 策略 ( https://github.com/h2database/h2database/pull/1912 )。但是 hibernate 的 H2Dialect 类没有相应更新(版本 5.4.9、5.4.10)。

我扩展了 H2Dialect 类来告诉 Hibernate org.hibernate.tool.schema.spi.SchemaMigrator 删除约束如下以解决问题。

更新:

spring:
    jpa:
        database-platform: mypackage.MyH2Dialect


import org.hibernate.dialect.H2Dialect;

public class MyH2Dialect extends H2Dialect {

    @Override
    public boolean dropConstraints() {
        return true;
    }

    @Override
    public boolean supportsIfExistsAfterAlterTable() {
        return true;
    }

}
Run Code Online (Sandbox Code Playgroud)

相关问题:

https://hibernate.atlassian.net/browse/HHH-13711

https://github.com/hibernate/hibernate-orm/pull/3093

https://github.com/h2database/h2database/pull/1912

另见: org.hibernate.tool.schema.internal.StandardForeignKeyExporter.getSqlDropStrings()


Mel*_*kor 0

我想让您知道我找到了解决此问题的方法。通过javax.persistence属性,您可以定义一个删除脚本,该脚本将在自动模式创建之前执行。

这使您能够指定必须删除表的顺序(首先是从属表)。也许尝试这些可以帮助你。请注意,它们替换了spring.jpa.hibernate.ddl属性。

spring.jpa.properties.javax.persistence.schema-generation.database.action=drop-and-create
spring.jpa.properties.javax.persistence.schema-generation.drop-source=script-then-metadata
spring.jpa.properties.javax.persistence.schema-generation.drop-script-source=drop-tables.sql
Run Code Online (Sandbox Code Playgroud)