Spring Boot 2.7 R2DBC测试初始化H2库报SQL约束不存在错误
问题根因
你遇到的错误码90057-212是H2数据库抛出的标准语法错误,核心触发逻辑是外键引用的目标列不存在主键或唯一约束,和Spring Boot、R2DBC的上层逻辑无关,具体诱因分两类:
- 最高发原因:
account表建表语句中的id字段仅配置了自增属性,未显式声明主键约束。H2不会自动给自增列绑定主键/唯一约束,而SQL规范强制要求外键关联的目标列必须是主键或具备唯一约束,因此创建money_deposit_event表的外键时直接抛出约束不存在的异常。 - 次要诱因:R2DBC H2驱动默认关闭多语句执行支持,如果你直接读取整个
init.sql的文本内容作为单条SQL提交,会出现语句解析截断,比如account表的主键定义片段未被正确执行,最终触发相同的约束不存在错误。
修复方案
1. 修正init.sql建表语法
首先确保account表的id字段显式声明主键,参考正确写法:
-- 错误示例:仅设置自增,无主键约束 -- CREATE TABLE account ( -- id BIGINT AUTO_INCREMENT, -- user_name VARCHAR(255), -- balance DECIMAL(10,2) -- ); -- 正确建表语句 CREATE TABLE account ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_name VARCHAR(255) NOT NULL, balance DECIMAL(10,2) NOT NULL DEFAULT 0 ); CREATE TABLE money_deposit_event ( id BIGINT AUTO_INCREMENT PRIMARY KEY, account_number BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL CHECK (amount > 99), CONSTRAINT fk_deposit_account FOREIGN KEY (account_number) REFERENCES account(id) ); -- 后续插入account表初始数据的语句保持原有逻辑即可
2. 配置R2DBC多语句支持,优化初始化逻辑
在测试环境配置文件application-test.properties中添加配置,开启H2 R2DBC驱动的多语句执行能力,同时配置内存库的生命周期规则:
# H2内存库连接配置,开启多语句支持,库在JVM退出前不关闭 spring.r2dbc.url=r2dbc:h2:mem:///testdb;DB_CLOSE_DELAY=-1;MULTI_STATEMENTS=TRUE spring.r2dbc.username=sa spring.r2dbc.password=
如果你不想手动在@BeforeAll方法中编写脚本读取、执行逻辑,可以直接使用Spring Boot 2.7自带的R2DBC初始化能力:将init.sql重命名为schema.sql放在test资源目录下,添加如下配置即可自动完成表结构初始化,不需要自定义DatabaseClient执行逻辑:
# 开启测试环境R2DBC脚本自动初始化 spring.r2dbc.initialization-mode=always
3. 校验结果
修复后重新执行测试用例,外键约束可以正常创建,StepVerifier会正常收到onComplete()信号,不会再抛出BadSqlGrammarException。
补充排查点:如果修复后依然报错,检查
init.sql中是否存在建表顺序颠倒、表名/字段名大小写不匹配的问题,H2默认对未加引号的标识符转大写,如果你在实体类中用了小写表名记得给表名、字段名加引号,或者在连接串中添加DATABASE_TO_LOWER=TRUE参数适配。
内容的提问来源于stack exchange,提问作者PAA
相关产品推荐
相关产品推荐

