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

Jackson序列化JPA双向关联时如何避免重复字段问题

Jackson 2.13.0 + Spring Boot JPA实体序列化问题解决方案

问题场景

运行环境为Jackson 2.13.0搭配Spring Boot框架,需要对JPA实体ProjectPhase执行序列化操作,实体间存在Project与ProjectPhase的一对多双向关联,当前需解决两个问题:

  1. 序列化ProjectPhase时,不需要输出其中嵌套的project关联字段
  2. Jackson序列化JPA双向关联关系时,需要规避字段重复输出、循环引用问题

相关实体代码

ProjectPhase实体代码:

@Entity
@Data @NoArgsConstructor @AllArgsConstructor
@Builder(toBuilder = true)
@JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use = JsonTypeInfo.Id.NAME)
public class ProjectPhase {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id_phase;
    private String phaseName;
    private Date dateDebt;
    private Date dateFin;
    @OneToMany(mappedBy = "projectPhase")
    private List<TypePhase> typesPhase;
    @ManyToOne
    private Project project;

    @OneToMany(mappedBy = "projectPhase")
    private List<Collab_Task_ProjectPhase> collab_task_projectPhases;
}

当前ProjectPhase列表序列化输出结果:

[
  {
    "ProjectPhase": {
      "id_phase": 1,
      "phaseName": "phase1",
      "dateDebt": null,
      "dateFin": null,
      "typesPhase": [],
      "project": {
        "Project": {
          "id": 1,
          "actif": true,
          "dateDebut": "2022-06-03T10:13:02.975+00:00",
          "dateFin": null,
          "projectName": "project 1"
        }
      },
      "collab_task_projectPhases": []
    }
  },
  {
    "ProjectPhase": {
      "id_phase": 2,
      "phaseName": "phase2",
      "dateDebt": null,
      "dateFin": null,
      "typesPhase": [],
      "project": {
        "Project": {
          "id": 1,
          "actif": true,
          "dateDebut": "2022-06-03T10:13:02.975+00:00",
          "dateFin": null,
          "projectName": "project 1"
        }
      },
      "collab_task_projectPhases": []
    }
  }
]

关联的Project实体代码:

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@JsonRootName(value = "project")
@JsonTypeInfo(include= JsonTypeInfo.As.WRAPPER_OBJECT,use = JsonTypeInfo.Id.NAME)
public class Project implements Serializable {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String ProjectName;
    private boolean actif;
    private Date dateDebut;
    private Date dateFin;
    //Status Enum (Dev Liv ...)
    @OneToMany(mappedBy = "project",cascade = CascadeType.ALL)
    @JsonIgnore
    private List<ProjectPhase> projectPhases;

}

具体解决方案

问题1:序列化ProjectPhase时排除project字段

直接在ProjectPhase实体的project字段上添加@JsonIgnore注解即可,该注解标记的字段会在序列化、反序列化流程中被完全忽略:

@ManyToOne
@JsonIgnore // 新增注解
private Project project;

如果需要保留反序列化时接收project参数的能力、仅在序列化时忽略该字段,使用@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)替代@JsonIgnore即可:

@ManyToOne
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Project project;

修改后序列化ProjectPhase时,将不再输出嵌套的project字段内容。

问题2:双向关联序列化规避重复字段/循环引用

JPA双向关联场景下,仅在单侧加@JsonIgnore虽然能阻断循环,但会导致被忽略侧的关联字段完全无法序列化,可根据业务场景选择以下两种通用方案:

方案1:使用@JsonManagedReference + @JsonBackReference配对注解

该方案通过明确双向关联的序列化层级阻断循环:

  • 在父级实体(持有关联集合的一方,即Project类的projectPhases字段)上添加@JsonManagedReference,标记为正向引用,序列化时正常输出
  • 在子级实体(持有单个关联对象的一方,即ProjectPhase类的project字段)上添加@JsonBackReference,标记为反向引用,序列化时直接跳过,避免循环
    当前Project的projectPhases字段上添加了@JsonIgnore,如果需要序列化Project时带出关联的阶段列表,将该注解替换为@JsonManagedReference即可:
// Project实体对应字段修改
@OneToMany(mappedBy = "project",cascade = CascadeType.ALL)
@JsonManagedReference // 替换原有@JsonIgnore
private List<ProjectPhase> projectPhases;
// ProjectPhase实体对应字段修改
@ManyToOne
@JsonBackReference
private Project project;

修改后效果:

  • 序列化Project时,会正常输出下属的projectPhases列表,列表内的ProjectPhase对象不会重复嵌套project字段,同时解决循环递归和字段重复问题
  • 序列化ProjectPhase时,不会输出关联的project字段,直接满足第一个问题的需求

方案2:使用@JsonIdentityInfo实现ID引用

如果业务要求双向都保留关联字段、不希望某一侧关联被完全忽略,可以在两个实体类上添加@JsonIdentityInfo注解,让Jackson遇到重复序列化的对象时,直接输出对象ID而非重复序列化完整内容,从根源避免循环和冗余:

// Project类顶部添加注解
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
// 其余原有注解保持不变
public class Project implements Serializable {
    // 类内容不变
}
// ProjectPhase类顶部添加注解
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id_phase")
// 其余原有注解保持不变
public class ProjectPhase {
    // 类内容不变
}

修改后效果:

  • 第一次序列化某个实体对象时会输出完整字段,后续再引用同一个对象时,仅输出该对象配置的主键(即上面配置的id/id_phase字段)
  • 不需要忽略任何关联字段,双向关联关系都会保留,也不会出现无限递归、同一对象重复序列化的问题

提示:如果选择该方案但仍不需要ProjectPhase序列化时输出project字段,依然可以配合@JsonIgnore或@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:01:06