Spring Boot初始化H2数据库时遭遇参照完整性约束违反问题
1. Hibernate自动DDL与初始化脚本冲突
如果项目配置了spring.jpa.hibernate.ddl-auto=create或create-drop,Spring Boot会先执行schema-h2.sql创建表,随后Hibernate会根据实体类重新生成DDL并重建表——这会覆盖之前创建的表。data-h2.sql的第一个INSERT语句会插入到被Hibernate删除的旧表中,数据直接丢失;而后续INSERT语句执行时,Hibernate已经完成表重建,数据能正常保留。同时,Hibernate生成的表结构可能和schema-h2.sql定义的外键约束不一致,导致插入时触发参照完整性违反错误。
2. SQL脚本语句分隔符解析异常
Spring Boot用ScriptUtils执行SQL脚本,默认以分号作为语句分隔符。如果data-h2.sql的第一个INSERT语句存在格式问题(比如分号被多行注释包裹、语句末尾漏加分号、特殊字符干扰分隔符识别),会导致第一个语句无法被正确解析执行,但手动执行时H2的解析逻辑更宽松,所以能正常运行。
3. 脚本编码的BOM干扰
如果data-h2.sql采用UTF-8 BOM编码,Spring Boot的脚本解析器会把BOM字符误认为SQL语句的一部分,导致第一个INSERT语句解析失败,无法插入数据,后续语句则不受影响。
1. 禁用/调整Hibernate自动DDL配置
将spring.jpa.hibernate.ddl-auto设置为none,完全禁用Hibernate的自动DDL生成,确保仅使用schema-h2.sql定义的表结构;或设置为validate,让Hibernate仅验证实体类与现有表结构的一致性,避免重建表。
示例配置(application.properties):
spring.jpa.hibernate.ddl-auto=none spring.sql.init.schema-locations=classpath:schema-h2.sql spring.sql.init.data-locations=classpath:data-h2.sql spring.sql.init.mode=always
2. 修正SQL脚本格式
- 确保
data-h2.sql中每个INSERT语句末尾都有正确的分号分隔符,且分号未被注释或特殊字符干扰; - 保证插入顺序符合参照完整性:先插入被参照表(如
ingredient_type)的数据,再插入依赖它的表(如ingredient)的数据。
示例正确的DML顺序:
INSERT INTO ingredient_type (id, name) VALUES ('WR', 'Wraps'); INSERT INTO ingredient_type (id, name) VALUES ('PR', 'Proteins'); INSERT INTO ingredient (id, name, type_id) VALUES ('FLTO', 'Flour Tortilla', 'WR'); INSERT INTO ingredient (id, name, type_id) VALUES ('GRBF', 'Ground Beef', 'PR');
3. 修正脚本编码格式
将data-h2.sql转换为UTF-8无BOM编码(可通过Notepad++等工具设置编码为“UTF-8”而非“UTF-8 BOM”),避免解析器被BOM字符干扰。
4. 自定义脚本分隔符(可选)
如果脚本中存在分号作为普通字符的情况(如字符串包含分号),可通过配置指定自定义分隔符:
spring.sql.init.separator=;;
同时将脚本中的语句分隔符统一改为;;。
内容的提问来源于stack exchange,提问作者David Tonhofer

