关于SSH 持久对象转Json双向关联解析过度的解决办法

小弟是一名被部分程序员所鄙视的培训狗,昨天做项目遇到一个问题,在SSH框架通过ajax向界面传递数据的时候,由于对象之间双向关联导致json解析过度,造成内存溢出,网上搜索了解决办法除了过滤某些关联对象之外就没有别的办法了,我的想法是通过设置解析深度来解决过度抓取的问题,开始也考虑性能问题,最好的解决办法就是做一个视图,单独写一个model 。但是又不希望这样繁琐,后来发现换个观点一想,能解决这个过度解析的问题对系统的性能的提升其实也很大了,所以就自己动手准备写一个转换工具类,办法也很明了递归、反射,使用一个参数来判断递归深度。

  编写的时候也做了很多的考虑比如持久对象中的关联对象实际是一个代理对象,怎么获取字段的值,后来通过分析代理对象的类名,发现hibernate代理对象的类名其实是在被代理对象名后加上以“_”开始的随机字符串(应该是随机的字符串吧),那么通过clazzName.split(“_”)[0]就解决了类名的问题,而且不论是持久对象还是原始的model对象都适用(当然这是类名里面没有“_的情况”,当然也可以通过截取字符串来得到,图方便我用的这个方法)。

  解决类名问题后想到我要获取class的所有属性,那么先要得到原始model的class实例,联想到jdbc载入driver的时候使用的 Class.forName(className),于是测试是否可以通过这个方法获取class实例,结果是肯定的,那么通过反射也就解决了获取原始类set、get方法问题,其中我又考虑到代理对象是否能够获取相同的get方法,因为我考虑的是值的复制,所以需要get代理对象属性的值,测试结果也是肯定的,后来又考虑到对象中其实是存在二级代理对象的问题,于是想到了递归,前面说到方法解决了实体类和代理类都可以获得get方法,那么通过递归就可以实现第二次的解析;

  解决model类和model的代理类的问题之后,发现其中可能会有set代理对象的问题,我们的项目中一对多都是使用的set,那么我就需要对set进行一个单独的处理,通过测试发现,set代理对象是hibernate中某个类的实例,那么就好办了,凡是事该类的实例我就对他使用copyset方法来处理,把set中的值都拷贝到一个model的set中,方法也简单遍历 调用处理单个对象的方法;

  当我把所有的步骤写完之后发现反射查找set方法出现问题,因为set方法是有参数的,那么获取参数的class又是我的目的了,想到我已经从代理对象取出了数据,那么我只需要getClass就可以获取他的类型了,测试ok,但是后来又出现问题,也就是代理类类型的问题,不过这已经不是问题了,因为上面已经说了,只需要对类名进行一个处理就可以解决通过class.forName(className)就可以获取;

  解决完model代理对象问题之后,发现set的代理对象也是需要处理的,不过既然set代理对象是hibernate的某个类,那我只要是这个类就让类型是Set.class就解决了,hql时间获取出来也不是普通的date类型,我们的model里date是util.Date,获取的date是sql包下的时间戳类型,不过我想了一下既然可以放进属性里,那么一定是继承自util.date那好办了 这个类型我就让他的类型是util.Date ;

  经过测试只需要设置递归深度就可以解决过度解析的问题,你需要解析几层就设置几层,通用性也还ok,同学的项目只需要加一个类型判断就可以处理了,也完全解决了项目的要求,从开始写到写完 总过花了3个小时,时间也是不长,遇到的问题也不是特别困难,不过能够通过自己的思考解决这个问题也是要赞自己是条汉子,现在把代码贴下来供大家讨论,有什么不足还请大家指出;

  

package com.gxa.bj.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.collection.internal.PersistentSet;

/**
 * @author koudan125
 * 该类用来解决hibernate持久转为json数据时过度解析问题,通过layer来设置解析深度,深度大于2会导致反向解析问题
 * 目前没有比较好的办法解决,关联不是过于复杂的情况下效率应该影响不是太大;
 *
 */
public class ConverHelper {
    /**
     * @param layer 解析深度
     * @param t 解析对象
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * 最核心的方法,用来解析model;
     *
     */
    public static Object getCopy(int layer, Object t)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, NoSuchMethodException, SecurityException,
            IllegalArgumentException, InvocationTargetException {
        if (t == null)
            return null;
        if (layer > 0) {
            layer--;
        } else {
            return null;
        }
        Class<?> otClass = (Class<?>) t.getClass();
        String ntClassName = otClass.getName().split("_")[0];//通过观察发现代理对象和实体对象是有关联的 规律大概是“类名_字符串”,这样可以通过传入对象取实体对象的类名(包括传入实体对象);
        @SuppressWarnings("unchecked")
        Class ntClass = Class.forName(ntClassName);//获取实体对象class实例
        Object newT = ntClass.newInstance();//创建实体对象用于储存传入对象信息;
        Field[] fields = ntClass.getDeclaredFields();
        for (Field f : fields) {//反射获取对象属性拼出get、set方法
            String fieldName = f.getName();
            String getMethodName = "get"
                    + fieldName.substring(0, 1).toUpperCase()
                    + fieldName.substring(1);
            String setMethodName = "set"
                    + fieldName.substring(0, 1).toUpperCase()
                    + fieldName.substring(1);
            Method getMethod = otClass.getMethod(getMethodName);
            Object obj = getMethod.invoke(t);//获取代理对象中属性的值
            if (obj != null) {
                System.out.println("转换"+obj);
                System.out.println("转换"+obj.getClass());
                System.out.println("转换"+ntClass);
                String objClassName = obj.getClass().getName().split("_")[0];//通过观察发现代理对象和实体对象是有关联的 规律大概是“类名_字符串”,这样可以通过传入对象取实体对象的类名(包括传入实体对象);
                Class objClass=Class.forName(objClassName);//获取属性的class实例
                if(objClass.isAssignableFrom(PersistentSet.class))//如果属于代理类型set,hibernate代理对象是该类的实例
                objClass=Set.class;
                else if(objClass.isAssignableFrom(Timestamp.class))//处理数据库取到时间的问题
                objClass=Date.class;
                Method setmethod = ntClass.getMethod(setMethodName,objClass);//获取实体类中的set方法;
                if (obj instanceof String || obj instanceof Date
                        || obj instanceof Integer) {
                    setmethod.invoke(newT, obj);//如果属于基本类型直接set赋值
                } else if (obj instanceof Set) {//如果属于set调用setCopy方法
                    Set<Object> set = (Set<Object>) obj;
                    setmethod.invoke(newT, getSetCopy(set, layer));
                } else {//属于其他类型,也就是代理类型或者model类型使用getCopy方法;
                    setmethod.invoke(newT, getCopy(layer, obj));
                }
            }
        }
        return newT;
    }

    /**
     * @param set
     * @param layer
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * 处理set类型对象的复制;
     */
    public static Set<Object> getSetCopy(Set<Object> set, int layer)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, NoSuchMethodException, SecurityException,
            IllegalArgumentException, InvocationTargetException {
        if (layer <= 0) {
            return null;
        }
        layer--;
        Set<Object> newSet = new HashSet<>();
        for (Object obj : set) {
            newSet.add(getCopy(layer, obj));
        }
        return newSet;
    }

    /**
     * @param list
     * @param layer
     * @return
     * @throws ClassNotFoundException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * 处理复制list类型对象;
     * 我们项目中使用的都是query.list()所以主要调用该方法处理list
     */
    public static List<Object> getListCopy(List list, int layer)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, NoSuchMethodException, SecurityException,
            IllegalArgumentException, InvocationTargetException {
        if (list == null)
            return null;
        ArrayList<Object> arrayList = new ArrayList<>();
        for (Object obj : list) {
            arrayList.add(getCopy(layer, obj));
        }
        return arrayList;
    }
}
时间: 2024-08-04 14:16:17

关于SSH 持久对象转Json双向关联解析过度的解决办法的相关文章

PHP Json函数不能处理中文的解决办法

PHP5.2 新增的 json 功能是非常受欢迎的,但是经过测试发现,json_encode对中文的处理是有问题的:    不能处理GB编码,所有的GB编码都会替换成空字符:    utf8编码的中文被编码成unicode编码,相当于javascript的escape函数处理的结果.    为了能正确使用json,首先我们就应该在编码上采用utf8编码,然后再对json_encode的返回结果稍加处理就可以得到正确的结果了.?1. [代码]php代码    <?php/*PHP5.2 新增的 j

javaEE SSH框架对象转json问题

最近毕设需要给App端写接口,一般都是返回json格式的数据,但是将对象转为json时总是报错,之前做后台界面使用ajax的时候也出现过. 一.需求:返回学生信息.学生对应部门信息.学生对应班级信息json数据 (1)实体类 public class Student implements java.io.Serializable{ /** * */ private static final long serialVersionUID = 1L; private Integer id; priva

FastJson将json解析成含有泛型对象,内部泛型对象再次解析出错的解决办法(Android)

折腾小半天的问题,这里先感谢一下深圳的小伙子,远程帮我搞,虽然也没有搞出来==========FUCK 声明:Android开发下发生此异常,Java开发下并不会有这个问题 异常重现 简单说一下抛出异常的代码: (1)解析json代码如下: RecommendBean<RecommendListBean> obj = JSON.parseObject(jsonString, new TypeReference<RecommendBean<RecommendListBean>&

使用mysql innodb 使用5.7的json类型遇到的坑和解决办法

---------------------------------------------- #查询JSON的某个字段 select data -> '$.Host' from temp #创建虚拟列 ALTER TABLE temp ADD host varchar(128) GENERATED ALWAYS AS (json_extract(data,'$.Host')) VIRTUAL; #给虚拟列创建索引 ALTER TABLE temp ADD INDEX index_temp_hos

Spring mvc 返回JSON 在IE 下提示下载 解决办法

http://www.blogjava.net/iamlibo/archive/2013/11/21/406646.html ———————————————————————————————————————————————————————————————————— 在IE低版本下接收Spring MVC @ResponseBody 返回的JSON 会提示下载保存.在chrome下没有问题.解决办法 是: <mvc:annotation-driven ignoreDefaultModelOnRedi

关于使用jquery时,ie8下提示对象不支持的属性或方法的解决办法

转自:http://wapapp.baidu.com/auoong/item/538790fcbe87c834d7ff8cde 首先这个问题的前提是已经排除了常见的这个问题.下面说一种今天我碰到的一种情况. 浏览器报的错误: 1.在xp/win7系统下安装的是ie8的报的错误是:对象不支持此属性与方法. 2.在win8下报的错误是:对象不支持"toLowerCase"属性或方法,即使是调整成ie8模式也是这个错误. 出错的代码段为jquery的源代码,如下: var b=a.nodeN

复现一个典型的线上Spring Bean对象的线程安全问题(附三种解决办法)

问题复现 假设线上是一个典型的Spring Boot Web项目,某一块业务的处理逻辑为: 接受一个name字符串参数,然后将该值赋予给一个注入的bean对象,修改bean对象的name属性后再返回,期间我们用了 Thread.sleep(300) 来模拟线上的高耗时业务 代码如下: @RestController @RequestMapping("name") public class NameController { @Autowired private NameService n

在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法

今天遇到了一个在servlet的service方法中获取ServletContext对象出现java.lang.NullPointerException(空指针)异常,代码如下: 1 //获取ServletContext对象 2 ServletContext servletContext = this.getServletContext(); 这个问题很奇怪,也是第一次遇到,因为以前在servlet的doGet/doPost方法中要获取ServletContext对象时都是这样写的,也没有出现过

JavaWeb学习(5.5)---在Servlet使用getServletContext()获取ServletContext对象出现java.lang.NullPointerException(空指针)异常的解决办法

本文整理自孤傲苍狼大大的博客,,, 在servlet的service方法中获取ServletContext对象也行出现java.lang.NullPointerException(空指针)异常,代码如下: 1 //获取ServletContext对象 2 ServletContext servletContext = this.getServletContext(); 原来是重写了 init(ServletConfig)方法,但重写的init方法内部没有调用super.init(config);