Spring Boot中Team与Match关联JSON循环引用问题及解决
最近碰到一个挺典型的JPA+Jackson序列化坑:我的系统里Team(队伍)和Match(对战比赛)是双向关联的——Team有自己参与的matches集合,Match里又包含对战的两个Team(teamHome和teamAway)。查询Team的时候要展示它参与的所有Match,但直接序列化会陷入无限嵌套:返回的Match里有两个Team,每个Team又带着自己的matches数组,循环往复根本停不下来。
最开始我想着用@JsonBackReference注解,直接加在Team类的matches集合上,结果倒好,整个matches数组直接被隐藏了,完全不符合我要展示Team参与赛事的需求。我的目标很明确:展示Team的matches数组,但每个Match里的teamHome和teamAway不能再带它们自己的matches集合。
我的实体类关联关系
先贴一下当时的实体类代码,方便大家理解场景:
Match类(初始版本)
@Entity @Getter @Setter @Table(name="match") public class Match { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @ManyToOne @JoinColumn(name = "team_id_home") private Team teamHome; @ManyToOne @JoinColumn(name = "team_id_away") private Team teamAway; private int scoreTeamHome; private int scoreTeamAway; private int nrOfWinsNeeded; @Temporal(TemporalType.TIMESTAMP) @JsonFormat(pattern="yyyy-MM-dd HH:mm") private Date date; public Match() { } public Match(Team teamHome, Team teamAway, int scoreTeamHome, int scoreTeamAway, int nrOfWinsNeeded, Date date){ this.teamHome = teamHome; this.teamAway = teamAway; this.scoreTeamHome = scoreTeamHome; this.scoreTeamAway = scoreTeamAway; this.nrOfWinsNeeded = nrOfWinsNeeded; this.date = date; } }
Team类(初始版本)
@Entity @Getter @Setter @Table(name = "`team`") public class Team { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(nullable = false) private String name; @Column(nullable = false) private String country; @Column(nullable = false, name = "code_name") private String codeName; @JsonBackReference @OneToMany(cascade = CascadeType.ALL) @JoinColumn(name = "team_id") private Set<Match> matches; @JsonBackReference @OneToMany(mappedBy = "team") private Set<Player> players; public Team(){ } public Team(String name, String country, String codeName) { this.name = name; this.country = country; this.codeName = codeName; } }
最终解决办法
试了几种方式之后,发现用@JsonIgnoreProperties注解最贴合我的需求:在Match类的teamHome和teamAway字段上,添加@JsonIgnoreProperties({"matches"}),明确告诉Jackson序列化这两个Team对象时,忽略它们的matches集合。
修改后的Match类关键部分:
@ManyToOne @JoinColumn(name = "team_id_home") @JsonIgnoreProperties({"matches"}) private Team teamHome; @ManyToOne @JoinColumn(name = "team_id_away") @JsonIgnoreProperties({"matches"}) private Team teamAway;
这样处理之后,查询Team返回的JSON里,会正常展示该Team的所有Match数组;而每个Match里的teamHome和teamAway只会包含队伍的基本信息(id、name、country这些),不会再带出它们的matches集合,完美打破了循环引用,同时也满足了业务展示需求。
内容的提问来源于stack exchange,提问作者Appel Taart

