美文网首页
巧妙解决Hibernate Jackson 双向关联循环

巧妙解决Hibernate Jackson 双向关联循环

作者: Tony_HQ | 来源:发表于2016-11-18 06:15 被阅读0次
    问题来源

    若是使用hibernate 建立entity,产生互相关联的两个类(ManytoOne, OnetoMany)

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

    此时如果使用json转换工具(fastjson,jackson等)会出现死循环。
    网上很少说根本原因,绝大多数都在描述,因为json转换B时,发现set<A>需要转化,blabla之类。
    但是根本原因在于,hibernate如果设置了fetch为lazy模式,每次访问都会加载,如果按着网上说的原因,如果对应的值是null,是不会递归下去的。(待我明天求证一下)

    网上流传解决方法
    1. 简单的添加@JsonIgnore之类,静态过滤属性,
    2. 对于Jackson使用MixInAnnotation特性,加之AOP,实现动态过滤。
    3. 直接创建对应两类集合类VO,不适用此类特性。

    方法一简单,但是不适用,每个业务上运用到的属性可能都不一致,如果每个都建立对应类不如直接使用3
    方法二首先感觉是繁琐,再者就是性能问题(猜想,没测试,未来添加测试,理由是有大量的反射,每次都需要动态解释,如果可以在编译阶段生成,那性能不是问题)。
    方法三简单性能好,但是创建VO类太多,不适合维护。

    我的解决方法
    1. 巧妙使用constructor设置不需要关联对象为null,
    2. 配上@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })

    后者为了阻止Jackson在转化时触发hibernateLazyFetch机制
    前者一是为了是在HQL语句里使用 select new A(B,AProperties) from ... 来实现直接注入,而是set null 可以使Jackson不会递归进行这个转换。

    这样,我们只需要给每个类加上@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
    为你每个需要的组合添加一个constructor便好(无需新建类)
    很简单可以实现多表联查等复杂的映射问题

    未来我会添加更加详细的原理解释。

    实例

    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映射
    }
    

    放一个关联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映射
    

    相关文章

      网友评论

          本文标题:巧妙解决Hibernate Jackson 双向关联循环

          本文链接:https://www.haomeiwen.com/subject/inwdpttx.html