現(xiàn)象
在使用JPA進(jìn)行實體關(guān)系管理的時候,會產(chǎn)生無限循環(huán)的情況,如果使用fastjson來進(jìn)行序列化,則表現(xiàn)形式如下:
{ "address":{ "id":63, "name":"1address name", "person":{"$ref":".."}, "zipCode":"ZipCode01" }, "firstName":"0firstName", "id":69, "lastName":"0lastName"},{ "address":{ "id":64, "name":"2address name", "person":{"$ref":".."}, "zipCode":"ZipCode11" }, "firstName":"1firstName", "id":70, "lastName":"1lastName"}
重點是address.person
的值:{"$ref":".."}
如果你用的不是fastjson(它默認(rèn)會檢查該對象是否已經(jīng)存在在json文本中)而是其他一些json類庫,比如jackson,則會拋出java.lang.StackOverflowError
異常(無限循環(huán)產(chǎn)生的棧溢出所導(dǎo)致).
但是,哪怕你用的是fastjson,你也無法用js來解析{"$ref":".."}
.
解決思路
JSON.toJSONString(page,SerializerFeature.DisableCircularReferenceDetect)
重新設(shè)計實體關(guān)系,盡量避免雙向關(guān)聯(lián),使用RESTful進(jìn)行接口的暴露.(舉個例子來說)
實體類Person
public class Person { private String name; @Id @GeneratedValue private Long id; @ManyToMany @JoinColumn(name = "address_id") private List<Address> addresses; // ...... getter and setter}
實體類Address
public class Address { @Id @GeneratedValue private Long id; private String name; private String zipCode; // ...... getter and setter}
兩個實體類之間的關(guān)系為Many Person To Many Address,只在Person實體類中進(jìn)行關(guān)系的配置,避免雙向關(guān)聯(lián).
下面舉例說明使用RESTful來對資源進(jìn)行訪問的情況.
對于Person:
/persons
/persons/{person_id}
/persons/{person_id}/addresses
4 查詢某一個Person的某一個Address: /persons/{person_id}/addresses/{address_id}
如果要查詢一個Address有幾個Person: /persons?address.id=xxx
(帶分頁,自己設(shè)置pageSize)
對于Address:
/addresses
/addresses/{address_id}
以上是Person和Address的一些簡單接口.其中粗體部分為關(guān)聯(lián)查詢.
設(shè)計的思路就是要盡量避免雙向關(guān)聯(lián),然后把Person作為一個資源,把Address作為Person的一個子資源或者屬性.
上述Person中的1 2 將Address作為了屬性,查詢時可以通過參數(shù)傳遞進(jìn)去.而上述Person中的3 4 兩個接口則將Address作為一個子資源進(jìn)行管理.
如果要用Address來作為一個資源反查Person怎么辦?
在一個Address管理頁面,需求要求列出某一個住址下的Person:
/persons?address.id=xxx
請求,取得List<Person>
./persons?address.id_in=xxx1,xxx2,xxx3
接口的返回值進(jìn)行處理,取得List<Address>
和其對應(yīng)的List<Person>
總結(jié)
盡量避免雙向關(guān)聯(lián),使用更合理的API設(shè)計方式,合理區(qū)分子資源和屬性.
大大減少數(shù)據(jù)庫壓力!