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的使用我寫了一個示例程序。
關於學生信息管理系統可以參考我的博客 學生信息管理系統
完整示例源碼獲取請訪問
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章