You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate @ManyToMany双向关联用Set持久化时StackOverflowError问题

异常根因分析

这个栈溢出完全是Lombok @Data注解 + 双向关联 + HashSet的去重校验逻辑三者共同触发的循环调用问题,具体逻辑如下:

1. 前置前提

Lombok的@Data注解会默认给实体类生成包含所有字段的equals、hashCode、toString方法,你这两个实体的关联字段Set<Trigger> trigger和Set<Category> categories都会被包含在这三个方法的计算逻辑里,形成双向依赖。

2. 为什么Set会触发栈溢出

Collectors.toSet()默认返回的是HashSet实现,HashSet添加元素时会执行两个操作:

  • 调用元素的hashCode方法计算哈希值
  • 用equals方法判断集合中是否已存在相同元素

触发栈溢出的完整流程:

  • 首次持久化Trigger时,从数据库查出来的Category实例的trigger集合是空的(还没有关联任何Trigger),此时把Category加入Trigger的HashSet时,计算hashCode不会触发循环,能正常执行。持久化完成后Hibernate会自动维护双向关联,把新建的Trigger加入到对应Category的trigger集合中。
  • 第二次持久化新Trigger时,查出来的Category实例的trigger集合已经存储了第一次创建的Trigger实例。此时把Category加入新Trigger的HashSet时,计算Category的hashCode会遍历它的trigger集合,调用每个Trigger的hashCode;而Trigger的hashCode又会遍历它的categories集合调用每个Category的hashCode,直接形成无限递归,最终抛出StackOverflowError。

3. 为什么换成List就正常

List接口的实现类(比如ArrayList)添加元素时不会做去重校验,不会主动调用元素的hashCode和equals方法,也就不会触发上述的递归循环,所以能正常运行。


修复建议

  • 不要给JPA实体类使用@Data注解,改用@Getter+@Setter组合,自定义equals和hashCode方法,仅基于主键或者业务唯一键(比如Category的name、id)生成,不要包含关联属性。
  • 移除Category侧@ManyToMany注解的cascade = CascadeType.ALL配置,你的场景中Category是预定义数据,配置全级联会导致删除Trigger时连带删除关联的Category,不符合业务逻辑。

内容的提问来源于stack exchange,提问作者Dima Kovbasa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:39:02