标签:
小弟是一名被部分程序员所鄙视的培训狗,昨天做项目遇到一个问题,在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; } }
标签:
原文地址:http://www.cnblogs.com/koudan125/p/5058426.html