Hibernate基类外键异常:启动DDL执行失败问题排查
问题根源
你碰到的这个异常,本质是Hibernate在create模式下执行DDL的顺序出了问题:
- 它先执行了
drop table if exists administration.user_data cascade,这个语句会自动级联删除所有依赖user_data的对象——包括general.equipment表上的fk_core_record_info_user外键约束。 - 紧接着Hibernate又尝试执行
alter table general.equipment drop constraint fk_core_record_info_user,但这个约束已经被前面的CASCADE操作删除了,自然就抛出了异常。
这大概率是Spring Boot 2.2.0.M6(对应Hibernate 5.4.x早期版本)的一个bug:Hibernate处理映射超类(也就是你的RecordInfo)中的关联关系时,没有正确推断表的删除顺序,反而先删除了被依赖的user_data表,而非先删除依赖它的equipment表。
解决方案
方案1:升级Spring Boot到稳定版本
Spring Boot 2.2.0.M6是里程碑预览版,存在不少未修复的问题。建议升级到同系列的正式版(如2.2.13.RELEASE)或更高稳定版本,后续版本中Hibernate的DDL生成逻辑已经修复了这类顺序错误。
方案2:切换到create-drop模式(适合开发环境)
如果暂时无法升级框架,可以将hibernate.hbm2ddl.auto的值从create改为create-drop:
hibernate.hbm2ddl.auto=create-drop
create-drop模式会在应用启动时创建表结构,在应用关闭时按正确的依赖顺序删除表(先删依赖表,再删被依赖表),从根源上避免了create模式下的删除顺序错误。
方案3:添加级联删除注解(辅助修正顺序)
你可以在RecordInfo的modifiedBy关联上添加@OnDelete注解,帮助Hibernate正确推断删除顺序:
@ManyToOne(fetch= FetchType.LAZY) @JoinColumn(name = "MODIFIED_BY_ID", nullable = false, foreignKey=@javax.persistence.ForeignKey(name="fk_core_record_info_user")) @OnDelete(action = OnDeleteAction.CASCADE) protected UserData modifiedBy;
这个注解会明确告诉Hibernate,当删除UserData时要级联处理依赖它的记录,也能辅助框架调整DDL的执行顺序。
Hibernate表删除与重建顺序的规则
Hibernate主要根据实体间的外键依赖关系来决定DDL的执行顺序:
- 删除表时:优先删除依赖其他表的表(即包含外键引用的表),再删除被依赖的表(被其他表引用的表),这样能避免删除被依赖表时出现外键约束冲突。
- 创建表时:顺序完全相反,先创建被依赖的表(无外键或被引用的表),再创建依赖它的表,确保创建外键时目标表已经存在。
当存在继承关系(如你的Equipment继承RecordInfo)或循环依赖时,Hibernate可能无法正确推断顺序,这就需要通过升级框架、调整配置或添加注解来修正。
内容的提问来源于stack exchange,提问作者mjo

