使用 H2 脚本初始化设置 Spring 测试环境在插入简单 SQL 数据时失败:“唯一索引或主键冲突”

问题描述 投票:0回答:2

我有一些旧的 MySQL 脚本,我已将其迁移到 H2,现在我想用该数据初始化测试数据库。

首先是我的

src/test/resources/application.yaml

spring:
    datasource:
        url: jdbc:h2:mem:bbstatstest
        username: sa
        password: sa
        driverClassName: org.h2.Driver
    jpa:
        hibernate:
            ddl-auto: none
            naming:
                physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
        open-in-view: false
        properties:
            hibernate:
                format_sql: true
        show-sql: true
    sql:
        init:
            mode: always

Spring 配置 bean:

@SpringBootTest
@Sql({"/schema.sql", "/data.sql"}) // put files into src/test/resources 
class BasketballStatsSpringApplicationTests {

    @Test
    void contextLoads() {
    }
}

如您所见,我使用

schema.sql
data.sql
来完成工作(位置:
src/test/resources
)。

创建模式+表(schema.sql):

CREATE SCHEMA IF NOT EXISTS bbstatstest;

CREATE TABLE IF NOT EXISTS bbstatstest.GeoContexts (
  id INT NOT NULL,
  parent_id INT NULL DEFAULT NULL,
  name VARCHAR(50) NOT NULL,
  type ENUM('CONTINENT', 'COUNTRY', 'REGION', 'STATE', 'DISTRICT') NULL DEFAULT NULL,
  PRIMARY KEY (id),
  CONSTRAINT geocontexts_self_fk
    FOREIGN KEY (parent_id)
    REFERENCES bbstatstest.GeoContexts (id)
    ON DELETE NO ACTION
    ON UPDATE CASCADE);

插入一些大陆(data.sql):

INSERT INTO bbstatstest.GeoContexts (id, parent_id, name) VALUES
(1, NULL, 'Africa'),
(2, NULL, 'Antarctica'),
(3, NULL, 'Asia'),
(4, NULL, 'Europe'),
(5, NULL, 'North America'),
(6, NULL, 'Oceania'),
(7, NULL, 'South America');

结果:

org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [data.sql]: INSERT INTO bbstatstest.GeoContexts (id, parent_id, name) VALUES (1, NULL, 'Africa'), (2, NULL, 'Antarctica'), (3, NULL, 'Asia'), (4, NULL, 'Europe'), (5, NULL, 'North America'), (6, NULL, 'Oceania'), (7, NULL, 'South America')

    at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:282)
    at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254)
    at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:54)
    at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.execute(ResourceDatabasePopulator.java:269)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$9(SqlScriptsTestExecutionListener.java:362)
    at org.springframework.transaction.support.TransactionOperations.lambda$executeWithoutResult$0(TransactionOperations.java:68)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
    at org.springframework.transaction.support.TransactionOperations.executeWithoutResult(TransactionOperations.java:67)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:362)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$4(SqlScriptsTestExecutionListener.java:275)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:275)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:222)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.beforeTestMethod(SqlScriptsTestExecutionListener.java:165)
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:320)
    at org.springframework.test.context.junit.jupiter.SpringExtension.beforeEach(SpringExtension.java:240)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PRIMARY KEY ON BBSTATSTEST.GEOCONTEXTS(ID) ( /* key:1 */ 1, NULL, 'Africa', NULL)"; SQL statement:
INSERT INTO bbstatstest.GeoContexts (id, parent_id, name) VALUES (1, NULL, 'Africa'), (2, NULL, 'Antarctica'), (3, NULL, 'Asia'), (4, NULL, 'Europe'), (5, NULL, 'North America'), (6, NULL, 'Oceania'), (7, NULL, 'South America') [23505-224]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:520)
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:489)
    at org.h2.message.DbException.get(DbException.java:223)
    at org.h2.message.DbException.get(DbException.java:199)
    at org.h2.mvstore.db.MVPrimaryIndex.add(MVPrimaryIndex.java:120)
    at org.h2.mvstore.db.MVTable.addRow(MVTable.java:519)
    at org.h2.command.dml.Insert.insertRows(Insert.java:174)
    at org.h2.command.dml.Insert.update(Insert.java:135)
    at org.h2.command.dml.DataChangeStatement.update(DataChangeStatement.java:74)
    at org.h2.command.CommandContainer.update(CommandContainer.java:169)
    at org.h2.command.Command.executeUpdate(Command.java:256)
    at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:262)
    at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:231)
    at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94)
    at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java)
    at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261)
    ... 17 more

这些脚本显然是由

ScriptUtils.executeSqlScript
拾取并执行的。

我已经盯着它看了两个多小时了,但我不明白为什么这个相当简单的东西会给我带来问题。

怎么了?你如何解决这个问题?

我正在使用 Spring 6.1.5:spring-jdbc-6.1.5 和 H2:h2-2.2.224

spring spring-data h2 spring-jdbc spring-test
2个回答
0
投票

Spring Boot 应用程序,如文档所述 将自动使用

schema.sql
data.sql
来填充数据库。

像您一样添加

@Sql
现在将再次执行
schema.sql
data.sql
。由于您的
schema.sql
仅在表不存在时创建表(并且其中没有删除等),因此在启动Spring Boot应用程序时已经填充了表(这就是
@SpringBootTest
它引导完整的应用程序) 。现在它尝试再次插入数据,从而产生错误。


-1
投票

春天不是你的问题。

我认为你的问题是你使用的是mysql特定的sql,而H2不喜欢它。

具体来说,您不能使用 H2 插入多行。您需要单独执行每一行:

INSERT INTO bbstatstest.GeoContexts (id, parent_id, name) VALUES (1, NULL, 'Africa');

INSERT INTO ...

每行一个插入

© www.soinside.com 2019 - 2024. All rights reserved.