如何防止Hibernate删除多对多关联表中的记录?
解决Hibernate清理多对多关联表初始数据的问题
核心排查方向与解决步骤
1. 修正关联映射:将三元关联改为实体化中间表
你的场景是三个实体的关联,而非传统双实体多对多,Hibernate默认多对多映射无法正确处理第三个关联字段(FK_INPUTAMOUNTCONSTRAINT),这也是null值记录出现的根本原因。直接把关联表映射为独立实体,完全控制关联逻辑:
@Entity @Table(name = "INPUTVALUERULEARTICLERULEAMOUNTCONSTRAINT") public class InputValueRuleArticleRuleAmountConstraint { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(optional = false) @JoinColumn(name = "FK_INPUTVALUERULE") private InputValueRule inputValueRule; @ManyToOne(optional = false) @JoinColumn(name = "FK_ARTICLERULE") private ArticleRule articleRule; @ManyToOne(optional = false) @JoinColumn(name = "FK_INPUTAMOUNTCONSTRAINT") private InputAmountConstraint inputAmountConstraint; // 基于三个关联实体ID重写equals/hashCode,避免重复插入或误删 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; InputValueRuleArticleRuleAmountConstraint that = (InputValueRuleArticleRuleAmountConstraint) o; return Objects.equals(inputValueRule.getId(), that.inputValueRule.getId()) && Objects.equals(articleRule.getId(), that.articleRule.getId()) && Objects.equals(inputAmountConstraint.getId(), that.inputAmountConstraint.getId()); } @Override public int hashCode() { return Objects.hash(inputValueRule.getId(), articleRule.getId(), inputAmountConstraint.getId()); } // Getters & Setters }
在三个主实体中用@OneToMany关联该中间实体:
@Entity @Table(name = "INPUTVALUERULE") public class InputValueRule { // 其他字段 @OneToMany(mappedBy = "inputValueRule", cascade = CascadeType.PERSIST) private List<InputValueRuleArticleRuleAmountConstraint> associations = new ArrayList<>(); // 提供安全的关联添加方法,确保双向同步 public void addAssociation(InputValueRuleArticleRuleAmountConstraint association) { association.setInputValueRule(this); associations.add(association); } }
2. 排查关联集合的修改操作
Hibernate会根据内存中实体集合的状态同步数据库,即使未设置级联删除:
- 检查所有事务内代码,是否执行过
clear()、remove()或替换集合实例的操作? - 是否操作了多对多的被拥有方集合但未同步拥有方?Hibernate仅识别拥有方的集合状态,修改被拥有方后未同步会被判定为关联失效,触发删除。
3. 启用SQL日志定位触发点
开启Hibernate SQL日志,直接观察清理/插入操作的触发时机:
hibernate.show_sql=true hibernate.format_sql=true hibernate.use_sql_comments=true
通过日志可以明确看到是哪个业务方法触发了关联表的DELETE/INSERT,精准定位代码问题。
4. 加固数据库约束
给关联表的三个外键字段添加NOT NULL约束,Hibernate插入null值时会直接抛出异常,快速定位错误代码位置。
5. 验证缓存一致性
若使用二级缓存,Liquibase插入初始数据后缓存未失效,会导致Hibernate用旧缓存覆盖数据库数据。临时禁用二级缓存测试,或在启动后手动清空缓存。
内容的提问来源于stack exchange,提问作者Sébastien Vandamme
相关产品推荐
相关产品推荐

