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

Spring Boot中@ManyToMany关联抛出StackOverflowError的解决方法

解决JPA多对多关联导致StackOverflowError的方案

你的代码已经通过@JoinTable和mappedBy完成了多对多双向关联的基础配置,但出现栈溢出的核心原因是双向关联引发的循环引用——无论是JSON序列化时的递归遍历,还是自动生成的toString/equals/hashCode方法对关联集合的调用,都会导致无限递归直至栈内存耗尽。

以下是具体修复方案:

1. 处理JSON序列化循环引用

可以通过Jackson注解明确双向关联的主从关系,或直接忽略其中一方的关联字段:

方案A:使用@JsonManagedReference和@JsonBackReference

这组注解可指定序列化的主从方,从根源避免循环:

public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String userName;
    String password;
    @ManyToMany
    @JoinTable(
            name = "student_courses",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "course_id", referencedColumnName = "id")
    )
    @JsonManagedReference // 标记为主关联方,序列化时正常输出
    List<Course> courses;

    // 重写toString/equals/hashCode,仅使用自身基本属性
    @Override
    public String toString() {
        return "Student{" + "id=" + id + ", userName='" + userName + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(id, student.id) && Objects.equals(userName, student.userName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, userName);
    }
}
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String courseName;
    @ManyToMany(mappedBy = "courses")
    @JsonBackReference // 标记为从关联方,序列化时自动忽略
    List<Student> students;

    // 重写toString/equals/hashCode,仅使用自身基本属性
    @Override
    public String toString() {
        return "Course{" + "id=" + id + ", courseName='" + courseName + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Course course = (Course) o;
        return Objects.equals(id, course.id) && Objects.equals(courseName, course.courseName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, courseName);
    }
}

方案B:直接在从方添加@JsonIgnore

如果不需要序列化从方的关联集合,可直接在Course的students字段上添加@JsonIgnore,切断序列化循环:

public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;
    String courseName;
    @ManyToMany(mappedBy = "courses")
    @JsonIgnore // 序列化时忽略该集合,避免循环
    List<Student> students;

    // 同样需要重写toString/equals/hashCode方法
}

2. 避免方法递归调用

多数IDE自动生成的toString/equals/hashCode方法会包含关联集合对象,这会导致调用时无限递归(比如Course.toString()调用Student.toString(),而Student.toString()又调用Course.toString())。因此必须重写这些方法,仅使用实体自身的基本属性(如ID、名称),不要包含关联的集合字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 20:14:57