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

Spring Boot返回Student数据时出现JSON无限递归栈溢出错误求助

解决JPA实体JSON序列化无限递归(StackOverflowError)的有效方案

方案1:正确配置@JsonManagedReference与@JsonBackReference

你之前的尝试可能是注解位置错误,正确用法是在**一对多的一方(Student)的集合字段上标记主动引用,在多对一的一方(Student_Course)**的关联实体字段上标记被动引用:

修改Student类:

@Entity
public class Student {
    @Id
    private String student_id;
    private String name;
    @Column(unique = true)
    private String email;
    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JsonManagedReference // 标记主动序列化的端
    private List<Student_Course> studentCourses;
}

修改Student_Course类:

@Entity
public class Student_Course {
    @EmbeddedId
    private Student_Course_PK student_course_pk;
    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("studentId")
    @JsonBackReference // 标记被动端,序列化时会被忽略
    private Student student;
    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("courseId")
    private Course course;
    private Date enrollment_date;
}

方案2:使用@JsonIgnoreProperties阻断循环链

如果不想用Managed/BackReference组合,可以直接指定忽略关联对象中的循环字段:

在Student的集合字段上,忽略Student_Course中的student引用:

@Entity
public class Student {
    // ...其他字段
    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    @JsonIgnoreProperties("student") // 序列化时跳过Student_Course里的student属性
    private List<Student_Course> studentCourses;
}

或者在Student_Course的student字段上,忽略Student里的studentCourses集合:

@Entity
public class Student_Course {
    // ...其他字段
    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("studentId")
    @JsonIgnoreProperties("studentCourses")
    private Student student;
    // ...其他字段
}

方案3:完善DTO层转换(推荐)

你已经定义了StudentDTO,问题大概率出在DTO转换时没有剥离循环引用。确保DTO只保留业务需要的字段,避免直接映射关联实体的完整对象:

定义简化的StudentDTO:

public class StudentDTO {
    private String studentId;
    private String name;
    private String email;
    // 只返回课程关键信息,不包含Student引用
    private List<CourseSimpleDTO> enrolledCourses;

    // 课程简化DTO
    public static class CourseSimpleDTO {
        private String courseId;
        private String courseCode;
        private String courseName;
        private Float credits;
        private Date enrollmentDate;

        // getter/setter
    }

    // getter/setter
}

在Service层手动完成实体到DTO的映射,彻底切断循环链:

public List<StudentDTO> getAllStudents() {
    return studentRepository.findAll().stream().map(student -> {
        StudentDTO dto = new StudentDTO();
        dto.setStudentId(student.getStudent_id());
        dto.setName(student.getName());
        dto.setEmail(student.getEmail());
        
        List<StudentDTO.CourseSimpleDTO> courseDtos = student.getStudentCourses().stream().map(sc -> {
            StudentDTO.CourseSimpleDTO courseDto = new StudentDTO.CourseSimpleDTO();
            courseDto.setCourseId(sc.getCourse().getCourse_id());
            courseDto.setCourseCode(sc.getCourse().getCourse_code());
            courseDto.setCourseName(sc.getCourse().getName());
            courseDto.setCredits(sc.getCourse().getCredits());
            courseDto.setEnrollmentDate(sc.getEnrollment_date());
            return courseDto;
        }).collect(Collectors.toList());
        
        dto.setEnrolledCourses(courseDtos);
        return dto;
    }).collect(Collectors.toList());
}

方案4:使用@JsonIdentityInfo识别对象身份

给循环引用的实体类添加该注解,让Jackson序列化时识别对象唯一标识,避免重复序列化:

给Student类添加:

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "student_id")
public class Student {
    // ...原有字段不变
}

给Student_Course类添加:

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "student_course_pk")
public class Student_Course {
    // ...原有字段不变
}

序列化时,第一次出现的对象会完整输出,后续出现的同一对象会用ID代替,阻断循环递归。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:27:04