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集合,這個字段直接就被忽略了。
- 第一種方法,當用戶點擊班級詳情的時候,發送單獨的請求去獲取學生列表,直接返回學生列表,而不是返回班級對象。
- 第二種方法,我就是想返回班級對象,因爲我可能也要知道班級的詳情,裏面帶上學生列表,這樣更容易理解。但是現在的問題是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的使用我寫了一個示例程序。
關於學生信息管理系統可以參考我的博客 學生信息管理系統
完整示例源碼獲取請訪問