JPA编程中如何解决json序列化导致的循环依赖stackoverflow问题

JPA编程中经常会使用注解@OneToMany和@ManyToOne这样的注解,但是当我们通过Controller接口返回数据给前端使用的时候,在json序列化的过程中,如果两个对象相互依赖,json就会不停的解析,这样就会导致stackoverflow。
org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Infinite recursion (StackOverflowError) (through reference chain:…
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: …
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
Caused by: java.lang.StackOverflowError

例如班级与学生是一对多的关系,简化代码如下:

@QClass(name = "班级")
@Entity
@Table(name = "b_clazz")
public class Clazz extends BaseEntity {
    @OneToMany(mappedBy = "clazz")
    @JsonBackReference
    private Set<Student> students;//学生
}
@QClass(name = "学生")
@Entity
@Table(name = "b_student")
public class Student extends BaseEntity {
    @ManyToOne
    @JoinColumn(name = "clazz_id")
    @JsonManagedReference
    private Clazz clazz; //班级所属班级
}

对于@OneToMany和@ManyToOne导致的相互依赖

这种情况我们使用@JsonManagedReference和@JsonBackReference来处理,此时我们在页面上展示学生列表的时候可以放心的引用clazz班级对象。具体解析过程是,我们访问学生列表,序列化班级的时候遇到@JsonManagedReference注解会序列化班级,班级对象又包含学生列表当序列化学生列表的时候遇到@JsonBackReference的时候就不继续序列化学生列表了,这样就避免了循环依赖。

显然@JsonBackReference阻断了序列化依赖的继续传播。

但是很多人意识到这个问题是双向的。也就是说,刚才我们只讲了展示学生列表的时候序列化班级的问题,如果我们展示班级列表的时候需要同时得到学生的结果怎么操作?很显然返回班级列表的时候直接遇到标注有@JsonBackReference的Set students集合,这个字段直接就被忽略了。

  1. 第一种方法,当用户点击班级详情的时候,发送单独的请求去获取学生列表,直接返回学生列表,而不是返回班级对象。
  2. 第二种方法,我就是想返回班级对象,因为我可能也要知道班级的详情,里面带上学生列表,这样更容易理解。但是现在的问题是Set students这个集合已经被忽略了。一种方法就是自己建立DTO对象,这样会多出很多类,并且很多手动拼装的代码很容易出错。另外一种方法是加一个相似字段,比如Set studentsCol,代码如下:
@QClass(name = "班级")
@Entity
@Table(name = "b_clazz")
public class Clazz extends BaseEntity {
    @OneToMany(mappedBy = "clazz")
    @JsonBackReference
    private Set<Student> students;//学生
    
    @Transient
    @JsonIgnoreProperties(ignoreUnknown = true, value = {"clazz"})
    private Set<Student> studentsCol;//学生

    public Set<Student> getStudentsCol() {
        return students;
    }
}

注意冗余的对象一定要加上@Transient注解,避免对数据库产生影响。
这样可以避免建立DTO对象。

如果你在对象中引用列表,仍然遇到json循环依赖的问题。你可以试试这个注解

@Transient
@JsonIgnoreProperties(ignoreUnknown = true, value = {"students"})
private List<Clazz> clazzes= new ArrayList<>();

这个注解明确的告诉程序,当我返回班级列表的时候,班级对象里面的students集合我不需要。

如果你还是遇到循环依赖,还有终极大杀器@JsonIgnore ,很明显这个注解一加上就不报错了lol

@Transient
@JsonIgnore
private List<Clazz> clazzes= new ArrayList<>();

对于@OneToMany @ManyToOne @ManyToMany的使用我写了一个示例程序。
关于学生信息管理系统可以参考我的博客 学生信息管理系统
完整示例源码获取请访问
JPA编程中如何解决json序列化导致的循环依赖stackoverflow问题_第1张图片

你可能感兴趣的:(java,web)