Hibernate为何在saveOrUpdate()前执行删除操作?
Hibernate多对多关联保存异常分析与解决
嘿,我来帮你捋捋这个多对多保存的问题~先把你遇到的场景再明确下:
- 你给
Column和Element实体建了**多对多(m:n)**关系,指定Column作为关系拥有者 - 保存
Columns集合时,流程出现了异常苗头:- 第一次迭代:
- Hibernate顺利保存第一个
Column - 往
column_element中间表插了两条记录,这时候数据库状态完全符合预期
- Hibernate顺利保存第一个
- 第二次迭代:
- 保存第二个
Column的时候出问题了(虽然你没写完,但大概率是关联记录重复插入、或者关联没生效、甚至抛出了异常对吧?)
- 保存第二个
- 第一次迭代:
大概率踩了这些坑,给你对应解决思路:
1. 关联的Element实体没被正确管理
如果你的Element是还没被Hibernate持久化的瞬态对象,或者在迭代过程中变成了游离态,保存第二个Column时,Hibernate很可能会重复插入Element,或者乱处理关联关系。
- 解决办法:
要么提前把所有关联的Element先持久化(比如先调用session.save(element)),要么谨慎配置多对多的级联属性。比如别随便用CascadeType.ALL,尤其是Element是被多个Column共享的情况,推荐这么写:@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinTable(name = "column_element", joinColumns = @JoinColumn(name = "column_id"), inverseJoinColumns = @JoinColumn(name = "element_id")) private Set<Element> elements;
2. 双向关联没维护好
多对多如果是双向的(Element那边也有columns集合),你只给Column的elements加了元素,却没同步更新Element的columns集合,Hibernate的持久化上下文就会懵圈,导致第二次保存时关联逻辑出错。
- 解决办法:
在Column里加个添加Element的方法,同时维护双向关联:
而且绝对不要手动去插中间表,全交给Hibernate管就行。public void addElement(Element element) { this.elements.add(element); element.getColumns().add(this); // 别忘了同步反向关联! }
3. equals/hashCode方法写崩了
Hibernate管理集合的时候,靠equals和hashCode判断对象是不是同一个。如果这俩方法只拿主键判断,而瞬态的Element主键是null,就会导致集合里出现重复的Element,进而触发重复插入中间表记录。
- 解决办法:
基于业务唯一字段来实现这俩方法,比如Element的name是唯一的,就这么写:@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); }
4. 会话管理出问题了
如果你在循环里反复开关会话,或者没及时刷新会话,持久化上下文和数据库状态不同步,也会导致第二次保存时出问题。
- 解决办法:
尽量在同一个会话里完成所有Column的保存操作;必要时可以在每次保存后调用session.flush()同步数据库,但别太频繁,影响性能。
最后给你个调试小技巧
打开Hibernate的SQL日志,看看第二次迭代时执行的SQL语句,到底是重复插了中间表记录,还是触发了主键/外键约束错误;另外也可以用session.contains(element)检查下Element是不是处于持久态,方便定位问题。
内容的提问来源于stack exchange,提问作者oitathi
相关产品推荐
相关产品推荐

