Spring Boot+JPA项目Swagger测试API遇JSON无限递归问题求助
Spring Boot JPA JSON无限递归(StackOverflowError)排查与解决
核心原因
实体类之间的双向关联导致Jackson序列化时出现循环引用,反复序列化关联对象最终触发栈溢出。
排查与解决方法
1. 检查@JsonManagedReference/@JsonBackReference的使用正确性
这两个注解必须成对出现在双向关联的两端,且位置不能搞反:
@JsonManagedReference:加在一对多关系中"一"的一方的集合属性上,标识该端是主动序列化的一方@JsonBackReference:加在一对多关系中"多"的一方的关联对象属性上,标识该端会被忽略序列化,避免循环
示例代码:
// 一的一方(Patient) @Entity public class Patient { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @JsonManagedReference @OneToMany(mappedBy = "patient", cascade = CascadeType.ALL) private List<Appointment> appointments; // getter、setter、构造方法 } // 多的一方(Appointment) @Entity public class Appointment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private LocalDateTime appointmentTime; @JsonBackReference @ManyToOne @JoinColumn(name = "patient_id") private Patient patient; // getter、setter、构造方法 }
2. 使用@JsonIdentityInfo打破循环引用
如果存在多层双向关联,或者上述注解无效,可以给关联实体添加@JsonIdentityInfo,通过唯一ID标识实体,序列化时重复引用会直接用ID代替,避免递归:
示例代码:
@Entity @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Appointment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private LocalDateTime appointmentTime; @ManyToOne @JoinColumn(name = "patient_id") private Patient patient; // 其他字段与方法 } @Entity @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Patient { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToMany(mappedBy = "patient") private List<Appointment> appointments; // 其他字段与方法 }
3. 改用DTO(数据传输对象)返回结果
直接返回实体类容易暴露不必要的关联数据,也会引发序列化问题,建议创建DTO层,只封装前端需要的字段:
// 定义AppointmentDTO public class AppointmentDTO { private Long id; private LocalDateTime appointmentTime; private String patientName; // 构造方法、getter、setter } // 业务层转换实体为DTO @Service public class AppointmentService { @Autowired private AppointmentRepository appointmentRepository; public AppointmentDTO getAppointmentById(Long id) { Appointment appointment = appointmentRepository.findById(id) .orElseThrow(() -> new RuntimeException("Appointment not found")); AppointmentDTO dto = new AppointmentDTO(); dto.setId(appointment.getId()); dto.setAppointmentTime(appointment.getAppointmentTime()); dto.setPatientName(appointment.getPatient().getName()); return dto; } }
4. 全局配置Jackson序列化深度
如果不想修改实体类,可以在配置文件中限制Jackson的序列化深度,避免无限递归:
在application.yml中添加:
spring: jackson: serialization: max-depth: 2 # 根据实际关联层级调整数值 fail-on-empty-beans: false
额外检查点
- 确认所有双向关联的实体对都做了正确的序列化注解处理(比如Appointment与Doctor的关联也可能存在循环)
- 检查Swagger是否启用了全局序列化扩展,是否覆盖了实体类的注解配置
内容的提问来源于stack exchange,提问作者Nguyen Le Hoang Chinh Chinh
相关产品推荐
相关产品推荐

