巧妙解決Hibernate Jackson 雙向關(guān)聯(lián)循環(huán)

問題來源

若是使用hibernate 建立entity,產(chǎn)生互相關(guān)聯(lián)的兩個類(ManytoOne, OnetoMany)

class A{
private B b;
private String AProperties;
}
class B{
private set<A> a;
private String BProperties;
}

此時如果使用json轉(zhuǎn)換工具(fastjson,jackson等)會出現(xiàn)死循環(huán)。
網(wǎng)上很少說根本原因,絕大多數(shù)都在描述,因為json轉(zhuǎn)換B時,發(fā)現(xiàn)set<A>需要轉(zhuǎn)化,blabla之類。
但是根本原因在于,hibernate如果設置了fetch為lazy模式,每次訪問都會加載,如果按著網(wǎng)上說的原因,如果對應的值是null,是不會遞歸下去的。(待我明天求證一下)

網(wǎng)上流傳解決方法
  1. 簡單的添加@JsonIgnore之類,靜態(tài)過濾屬性,
  2. 對于Jackson使用MixInAnnotation特性,加之AOP,實現(xiàn)動態(tài)過濾。
  3. 直接創(chuàng)建對應兩類集合類VO,不適用此類特性。

方法一簡單,但是不適用,每個業(yè)務上運用到的屬性可能都不一致,如果每個都建立對應類不如直接使用3
方法二首先感覺是繁瑣,再者就是性能問題(猜想,沒測試,未來添加測試,理由是有大量的反射,每次都需要動態(tài)解釋,如果可以在編譯階段生成,那性能不是問題)。
方法三簡單性能好,但是創(chuàng)建VO類太多,不適合維護。

我的解決方法
  1. 巧妙使用constructor設置不需要關(guān)聯(lián)對象為null,
  2. 配上@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })

后者為了阻止Jackson在轉(zhuǎn)化時觸發(fā)hibernateLazyFetch機制
前者一是為了是在HQL語句里使用 select new A(B,AProperties) from ... 來實現(xiàn)直接注入,而是set null 可以使Jackson不會遞歸進行這個轉(zhuǎn)換。

這樣,我們只需要給每個類加上@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
為你每個需要的組合添加一個constructor便好(無需新建類)
很簡單可以實現(xiàn)多表聯(lián)查等復雜的映射問題

未來我會添加更加詳細的原理解釋。

實例

HQL語句

String hql="select new UserAccount(uau,ua.username) from UserAccount as ua left join ua.user where ua.userAccountId=?0";

兩個Entity

@Entity
@Table(name = "user_account", catalog = "yunshen", uniqueConstraints = { @UniqueConstraint(columnNames = "telephone"),
        @UniqueConstraint(columnNames = "email"), @UniqueConstraint(columnNames = "username") })
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler"})
public class UserAccount implements java.io.Serializable {
    private Integer userAccountId;
    private User user;
    private String username;
    private String password;
    private String salt;
    private String telephone;
    private String email;
    private Date addTime;
    private Date loginTime;
    private int status;

    public UserAccount() {
    }

    public UserAccount(User user, String username,String password) {
        user.setDepartment(null);
        user.setUserAccounts(null);
        user.setUserAuthorities(null);
        this.password=password;
        this.user = user;
        this.username = username;
    }

    
    public UserAccount(User user, String username, String password, String salt, Date addTime, int status) {
        this.user = user;
        this.username = username;
        this.password = password;
        this.salt = salt;
        this.addTime = addTime;
        this.status = status;
    }

    public UserAccount(User user, String username, String password, String salt, String telephone, String email,
            Date addTime, Date loginTime, int status) {
        this.user = user;
        this.username = username;
        this.password = password;
        this.salt = salt;
        this.telephone = telephone;
        this.email = email;
        this.addTime = addTime;
        this.loginTime = loginTime;
        this.status = status;
    }
//省略掉getter and setter, 對應column映射
}

放一個關(guān)聯(lián)Entity類

@Entity
@Table(name = "user")
@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler"})
public class User implements java.io.Serializable {
    private Integer userId;
    private Department department;  
    private String name;
    private String sex;
    private Date birthday;
    private String remark;
    private Set<UserAccount> userAccounts = new HashSet<UserAccount>(0);
    private Set<UserAuthority> userAuthorities = new HashSet<UserAuthority>(0);

    public User() {
    }
    public User( String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User [userId=" + userId + ", name=" + name + ", sex=" + sex
                + ", birthday=" + birthday + ", remark=" + remark ;
    }

    public User(Department department, String name, String sex) {
        this.department = department;
        this.name = name;
        this.sex = sex;
    }

    public User(Department department, String name, String sex, Date birthday, String remark,
            Set<UserAccount> userAccounts, Set<UserAuthority> userAuthorities) {
        this.department = department;
        this.name = name;
        this.sex = sex;
        this.birthday = birthday;
        this.remark = remark;
        this.userAccounts = userAccounts;
        this.userAuthorities = userAuthorities;
    }
//省略掉getter and setter, 對應column映射
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,740評論 18 399
  • 這部分主要是開源Java EE框架方面的內(nèi)容,包括Hibernate、MyBatis、Spring、Spring ...
    雜貨鋪老板閱讀 1,570評論 0 2
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,017評論 0 11
  • 本文中我們介紹并比較兩種最流行的開源持久框架:iBATIS和Hibernate,我們還會討論到Java Persi...
    大同若魚閱讀 4,437評論 4 27
  • “鄰居”這個概念,大致還有我這樣從農(nóng)村出來的人,才會分成若干個階段來進行理解。 先讓我?guī)慊氐?0...
    隱士阿立閱讀 518評論 1 0

友情鏈接更多精彩內(nèi)容