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

JPA双向关联中@JsonIgnoreProperties无效,如何解决JSON无限递归?

解决JPA双向关联JSON序列化无限递归问题(无需DTO/映射工具)

以下是几种无需创建DTO或使用映射工具的解决方案,直接通过Jackson注解和Spring特性实现期望的JSON结构:

方案1:使用@JsonIdentityInfo识别对象唯一标识

该注解让Jackson在序列化时记录对象的唯一ID,后续遇到同一对象时仅输出ID,避免循环嵌套。

在两个实体类上添加注解:

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import jakarta.persistence.*;
import java.util.List;

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Departamento {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    
    @OneToMany(mappedBy = "departamento")
    private List<Funcionario> funcionarios;
    
    // getter、setter省略
}

@Entity
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class Funcionario {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    
    @ManyToOne
    private Departamento departamento;
    
    // getter、setter省略
}

效果:第一次序列化对象时输出完整字段,后续重复出现的对象仅显示ID,既保留关联关系又避免递归。

方案2:使用@JsonView控制不同场景的序列化字段

定义视图接口区分不同查询场景,在实体字段上标注允许序列化的视图,最后在Controller中指定使用的视图。

  1. 定义视图接口
public class Views {
    public interface FuncionarioView {}
    public interface DepartamentoView {}
}
  1. 实体类标注视图
import com.fasterxml.jackson.annotation.JsonView;
import jakarta.persistence.*;
import java.util.List;

@Entity
public class Departamento {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView({Views.FuncionarioView.class, Views.DepartamentoView.class})
    private Long id;
    
    @JsonView({Views.FuncionarioView.class, Views.DepartamentoView.class})
    private String nome;
    
    @OneToMany(mappedBy = "departamento")
    @JsonView(Views.DepartamentoView.class)
    private List<Funcionario> funcionarios;
    
    // getter、setter省略
}

@Entity
public class Funcionario {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonView({Views.FuncionarioView.class, Views.DepartamentoView.class})
    private Long id;
    
    @JsonView({Views.FuncionarioView.class, Views.DepartamentoView.class})
    private String nome;
    
    @ManyToOne
    @JsonView(Views.FuncionarioView.class)
    private Departamento departamento;
    
    // getter、setter省略
}
  1. Controller指定视图
import com.fasterxml.jackson.annotation.JsonView;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/funcionarios")
public class FuncionarioController {
    private final FuncionarioRepository funcionarioRepo;

    public FuncionarioController(FuncionarioRepository funcionarioRepo) {
        this.funcionarioRepo = funcionarioRepo;
    }

    @GetMapping("/{id}")
    @JsonView(Views.FuncionarioView.class)
    public Funcionario getFuncionario(@PathVariable Long id) {
        return funcionarioRepo.findById(id).orElseThrow();
    }
}

@RestController
@RequestMapping("/departamentos")
public class DepartamentoController {
    private final DepartamentoRepository departamentoRepo;

    public DepartamentoController(DepartamentoRepository departamentoRepo) {
        this.departamentoRepo = departamentoRepo;
    }

    @GetMapping("/{id}")
    @JsonView(Views.DepartamentoView.class)
    public Departamento getDepartamento(@PathVariable Long id) {
        return departamentoRepo.findById(id).orElseThrow();
    }
}

效果:查询Funcionario时仅返回Departamento的基础字段,查询Departamento时仅返回Funcionario的基础字段,完全避免循环嵌套。

方案3:正确使用@JsonIgnoreProperties双向忽略关联字段

确保在双向关联的两端都忽略对方的关联属性,避免序列化时触发循环。

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.*;
import java.util.List;

@Entity
public class Departamento {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    
    @OneToMany(mappedBy = "departamento")
    @JsonIgnoreProperties("departamento")
    private List<Funcionario> funcionarios;
    
    // getter、setter省略
}

@Entity
public class Funcionario {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String nome;
    
    @ManyToOne
    @JsonIgnoreProperties("funcionarios")
    private Departamento departamento;
    
    // getter、setter省略
}

效果:序列化Departamento时,Funcionario中的departamento字段会被忽略;序列化Funcionario时,Departamento中的funcionarios字段会被忽略,彻底阻断递归链。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 04:46:12