使用 @Sql & H2 的 Spring Boot 测试给出了 JdbcSQLIntegrityConstraintViolationException: 唯一索引或主键冲突

enh*_*ack 2 testing hibernate h2 unique-constraint spring-boot-test

我有一个简单的 Spring Boot (2.6.1) 测试,它抛出:

 JdbcSQLIntegrityConstraintViolationException: 
 Unique index or primary key violation
Run Code Online (Sandbox Code Playgroud)

该测试从 加载数据test/resources/data.sql,其中只有一条语句:

INSERT INTO country (name) VALUES ('Italy');
Run Code Online (Sandbox Code Playgroud)

测试类是:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class FailingTest {

   @Test
   @Sql("/data.sql")
   void test(){
       assertTrue(true);
   }
}
Run Code Online (Sandbox Code Playgroud)

域类是:

@Entity 
public class Country {
   @Id
   @GeneratedValue(strategy = IDENTITY)
   private Integer id;

   @Column(nullable = false, unique = true)
   private String name;
}
Run Code Online (Sandbox Code Playgroud)

application.properties文件包含:

spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.jpa.defer-datasource-initialization=true
Run Code Online (Sandbox Code Playgroud)

异常消息是:

org.springframework.jdbc.datasource.init.ScriptStatementFailedException:
Failed to execute SQL script statement #1 of class path resource [data.sql]: 
INSERT INTO country (name) VALUES ('Italy'); 
nested exception is org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: 
Unique index or primary key violation: 
"PUBLIC.UK_LLIDYP77H6XKEOKPBMOY710D4_INDEX_6 ON PUBLIC.COUNTRY(NAME) 
VALUES ( /* key:1 */ 2, 'Italy')"; 
SQL statement:INSERT INTO country (name) VALUES ('Italy') [23505-200]
Run Code Online (Sandbox Code Playgroud)

应用程序中没有其他 sql 脚本,sql 数据也不会以编程方式加载,并且国家/地区不会其他实体引用。该测试作为单个测试运行。

看来 INSERT 语句执行了两次,这违反了列的唯一条件。但为什么?!

当我更换时@Column(nullable = false, unique = true)一切@Column(nullable = false)正常!

有什么想法吗?解决方法?

Sem*_*kov 7

这里的问题是关于 Spring Boot 自动配置功能的。框架将名为data.sql默认初始化程序的文件视为默认初始化程序。这里实际发生了什么:

  1. 完全配置应用程序上下文后,Spring Boot 将data.sql作为初始 SQL 源执行。
  2. 当 JUnit 调用方法时,由于注释的存在test(),它会尝试再次执行。data.sql@Sql
  3. 违反了唯一约束。抛出异常。

有多种方法可以处理该问题:

  1. 更改文件名(例如,init-data.sql)。在这种情况下,Spring 不会在应用程序上下文启动时执行它。
  2. 将 Spring Bootspring.datasource.initialization-mode属性更改为never.
  3. 将脚本移动到任何子文件夹。