用于测试 Spring Boot 应用程序的初始化 H2 数据库期间出现异常“org.h2.jdbc.JdbcSQLSyntaxErrorException:未找到表“NAME””

nik*_*ava 4 h2 spring-jdbc spring-data-jpa spring-boot spring-boot-test

我编写用@SpringBootTest注释的集成测试。为了运行测试,我将测试资源中的 data.sql 中的数据插入到 H2 数据库中。

\n

我遇到的情况是,第一次测试成功运行,经过多次后,出现错误,告诉我,H2 无法在表中插入数据,因为找不到该表。每次启动测试时都会出现不同的原始错误。在同一日志中,我看到在异常发生之前该表已创建,并且其他插入已成功执行。我看到我的 5 个测试中有 4 个是绿色的,一个是红色的。不同的时间可能有不同的测试。

\n

重新启动 IDE 可使测试再次成功运行 3-4 次。之后,错误返回。

\n

我尝试将 @DirtiesContext() 与我的测试类和测试方法一起使用,但它没有解决问题。

\n

我猜测问题的根源可能在于我初始化数据库的方式。对于这两种数据库,我都使用一个 data.sql 文件的引用。我没有找到一种方法将它们分成不同的 *.sql 文件。

\n

第二个猜测是在创建表之前就开始插入数据库。我现在通过将所有插入移动到测试代码中来检查这个理论。但我不确定这是否会有所帮助,因为我播种日志是在插入开始之前创建的表。

\n

我在每次测试运行之前使用“mvn clean”。

\n

我将非常感谢您在解决这个问题方面的建议。

\n

我的实体:

\n
@Entity\n@Table(name = "entity1", schema = "schema1")\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class SomeEntityClass extends GenericEntity<Long> { ...}\n\n\n@MappedSuperclass\npublic abstract class GenericEntity<ID extends Serializable> implements Serializable {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    @Column\n\n    protected ID id;\n\n//getter, setter\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

在我的 data.sql 文件中初始化看起来像这样:

\n
create schema if not exists schema1;\ncreate schema if not exists schema2; \n\ndrop table if exists schema1.table1;\ncreate table schema1.table1\n(....structure of the table...)\n \ndrop table if exists schema2.table2;\ncreate table schema2.table2\n(....structure of the table...)\n \nINSERT INTO schema1.table1\n(...)\nVALUES (...)\n \n... many insertions\n \n \nINSERT INTO schema2.table2\n(...)\nVALUES (...)\n \n... many insertions\n
Run Code Online (Sandbox Code Playgroud)\n

这是 test/resources 中 application.properties 中的 H2 配置:

\n
# The first database\nspring.datasource.url=jdbc:h2:mem:database01;INIT=RUNSCRIPT FROM 'src/test/resources/data.sql'\nspring.datasource.username=user1\nspring.datasource.password=abc\nspring.datasource.driver-class-name=org.h2.Driver\nspring.jpa.generate-ddl=true\n\n# The second database\nspring.datasource.security.url=\\\n  jdbc:h2:mem:dbo_security_db:database02;INIT=RUNSCRIPT FROM 'src/test/resources/data.sql'\nspring.datasource.security.username=user1\nspring.datasource.security.password=abc\nspring.datasource.security.driver-class-name=org.h2.Driver\n\nspring.jpa.hibernate.ddl-auto=update\nspring.jpa.properties.hibernate.format_sql=true\n
Run Code Online (Sandbox Code Playgroud)\n

记录错误:

\n
08-09-2021 09:00:08.729 DEBUG [o.s.jdbc.datasource.init.ScriptUtils] - 0 returned as update count for SQL: create table schema1.table1( \xe2\x80\xa6.)\n08-09-2021 09:00:08.730 DEBUG [o.s.jdbc.datasource.init.ScriptUtils] - 1 returned as update count for SQL: INSERT INTO schema1.table1(...) VALUES ()\n///...other insertions into this table performed well\n08-09-2021 09:00:08.735 WARN  [o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext] - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #36 of URL [file:/home/nikiforov-java/Documents/.../target/test-classes/data.sql]: INSERT INTO schema1.table1(...) VALUES (...); nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "TABLE1" not found; SQL statement: ...\n\nError starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.\n08-09-2021 09:00:08.782 ERROR [o.s.boot.SpringApplication] - Application run failed\n\n
Run Code Online (Sandbox Code Playgroud)\n

小智 6

将此属性添加到application.properties

spring.jpa.defer-datasource-initialization=true
Run Code Online (Sandbox Code Playgroud)

根据Spring Boot 2.5 RELEASE 注释

默认情况下,data.sql脚本现在在 Hibernate 初始化之前运行。这使基于脚本的基本初始化的行为与 Flyway 和 Liquibase 的行为保持一致。如果您想用于data.sql填充 Hibernate 创建的架构,请设置spring.jpa.defer-datasource-initializationtrue