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

从数据库获取数据时出现JSON无限递归StackOverflowError问题求助

解决Jackson序列化无限递归(StackOverflowError)问题

问题原因

你的Task和User实体存在双向关联:

  • Task通过assignee字段引用User
  • User通过tasks字段引用List<Task>
    当Jackson尝试将实体序列化为JSON时,会不断在Task -> User -> Task -> User之间循环遍历,最终触发StackOverflowError。

解决方案

方法1:使用Jackson双向关联注解

方案1a:用@JsonIgnore忽略反向关联

在User的tasks字段上添加@JsonIgnore,序列化User时会跳过tasks列表,打断循环:

@Entity
@Table(name = "tbl_user")
public class User {
    // ... 其他字段
    @JsonIgnore
    @OneToMany(mappedBy = "assignee", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Task> tasks;
    // ...
}

方案1b:用@JsonManagedReference和@JsonBackReference区分主次关联

  • 在主动序列化的关联方(如Task的assignee)添加@JsonManagedReference
  • 在反向关联方(User的tasks)添加@JsonBackReference,该字段会被Jackson忽略
// Task类
@ManyToOne
@JoinColumn(name = "user_id")
@JsonManagedReference
private User assignee;

// User类
@JsonBackReference
@OneToMany(mappedBy = "assignee", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Task> tasks;

方法2:完善DTO映射(推荐)

你已经在使用TaskResponse作为返回DTO,确保映射时只保留前端需要的字段,不要直接传递整个实体对象:

// TaskResponse示例
public class TaskResponse {
    private Long taskId;
    private String title;
    private String description;
    private Status status;
    // 只保留User的必要属性,而非整个User对象
    private Long assigneeId;
    private String assigneeName;
    private String assigneeEmail;
    private List<CommentResponse> comments;

    // getter、setter方法
}

// 调整映射逻辑
private TaskResponse mapToTaskResponse(Task task) {
    TaskResponse response = new TaskResponse();
    response.setTaskId(task.getTaskId());
    response.setTitle(task.getTitle());
    response.setDescription(task.getDescription());
    response.setStatus(task.getStatus());
    
    if (task.getAssignee() != null) {
        response.setAssigneeId(task.getAssignee().getUserId());
        response.setAssigneeName(task.getAssignee().getName());
        response.setAssigneeEmail(task.getAssignee().getEmail());
    }
    
    response.setComments(task.getComments().stream()
            .map(this::mapToCommentResponse)
            .collect(Collectors.toList()));
    return response;
}

这种方式从根源上避免了实体循环引用,同时符合DTO设计原则,只返回必要数据。

方法3:使用@JsonIdentityInfo保留关联关系

如果需要保留双向关联的序列化,可通过该注解让Jackson识别对象身份,重复对象仅输出ID:

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "taskId")
@Table(name = "tbl_task")
public class Task {
    // ...
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "userId")
@Entity
@Table(name = "tbl_user")
public class User {
    // ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:40:17