問題來源
若是使用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)上流傳解決方法
- 簡單的添加@JsonIgnore之類,靜態(tài)過濾屬性,
- 對于Jackson使用
MixInAnnotation特性,加之AOP,實現(xiàn)動態(tài)過濾。 - 直接創(chuàng)建對應兩類集合類VO,不適用此類特性。
方法一簡單,但是不適用,每個業(yè)務上運用到的屬性可能都不一致,如果每個都建立對應類不如直接使用3
方法二首先感覺是繁瑣,再者就是性能問題(猜想,沒測試,未來添加測試,理由是有大量的反射,每次都需要動態(tài)解釋,如果可以在編譯階段生成,那性能不是問題)。
方法三簡單性能好,但是創(chuàng)建VO類太多,不適合維護。
我的解決方法
- 巧妙使用constructor設置不需要關(guān)聯(lián)對象為null,
- 配上
@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映射