如何用Jackson注解避免双向OneToOne无限递归并获取关联实体指定属性
解决双向@OneToOne关联的JSON序列化需求
你可以通过以下几种方式实现期望的JSON输出,同时避免无限递归:
方法一:在Certificate中添加只读序列化字段(最直接)
直接在Certificate类中新增一个仅用于序列化的方法,屏蔽原有的Student关联字段,同时返回包含studentId的嵌套结构:
修改后的Certificate类:
@Entity @Table(name = "certificate") public class Certificate { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int certificateId; @Column(name = "aknowledgment") private String aknowledgment; @OneToOne() @JoinColumn(name="student_id", nullable= false) @JsonIgnore // 屏蔽整个Student对象,避免递归 private Student student; // 自定义序列化字段,返回仅含studentId的结构 @JsonProperty("student") public Map<String, Integer> serializeStudentId() { Map<String, Integer> studentMap = new HashMap<>(); studentMap.put("studentId", student.getStudentId()); return studentMap; } // 省略原有getter、setter,注意不要给serializeStudentId添加setter }
同时去掉Student类中的@JsonManagedReference注解,因为已经通过@JsonIgnore阻断了递归路径。
方法二:使用@JsonIdentityInfo识别对象身份
让Jackson通过对象ID识别重复引用,自动在重复序列化时只输出ID:
修改Student类:
@Entity @Table(name = "student") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "studentId") public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int studentId; @Column(name = "age") private int age; @Column(name = "mean") private double mean; @OneToOne(mappedBy="student" ,cascade = CascadeType.ALL) private Certificate certificate; // 省略getter、setter }
修改Certificate类:
@Entity @Table(name = "certificate") @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "certificateId") public class Certificate { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private int certificateId; @Column(name = "aknowledgment") private String aknowledgment; @OneToOne() @JoinColumn(name="student_id", nullable= false) private Student student; // 省略getter、setter }
序列化Student时,嵌套的Certificate中的Student会自动只输出studentId,符合你的期望结构。
方法三:使用@JsonView控制序列化范围
先定义视图接口:
public class SerializationViews { public static class BasicView {} }
修改Student类,标记需要序列化的字段:
@Entity @Table(name = "student") public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @JsonView(SerializationViews.BasicView.class) private int studentId; @Column(name = "age") @JsonView(SerializationViews.BasicView.class) private int age; @Column(name = "mean") @JsonView(SerializationViews.BasicView.class) private double mean; @OneToOne(mappedBy="student" ,cascade = CascadeType.ALL) @JsonView(SerializationViews.BasicView.class) private Certificate certificate; // 省略getter、setter }
修改Certificate类,自定义student字段的序列化:
@Entity @Table(name = "certificate") public class Certificate { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @JsonView(SerializationViews.BasicView.class) private int certificateId; @Column(name = "aknowledgment") @JsonView(SerializationViews.BasicView.class) private String aknowledgment; @OneToOne() @JoinColumn(name="student_id", nullable= false) private Student student; @JsonView(SerializationViews.BasicView.class) @JsonProperty("student") public Map<String, Integer> getStudentId() { Map<String, Integer> studentMap = new HashMap<>(); studentMap.put("studentId", student.getStudentId()); return studentMap; } // 省略其他getter、setter }
最后在Controller接口上指定视图:
@GetMapping("/students/{id}") @JsonView(SerializationViews.BasicView.class) public Student getStudent(@PathVariable int id) { return studentRepository.findById(id).orElse(null); }
内容的提问来源于stack exchange,提问作者idash
相关产品推荐
相关产品推荐

