毕设问题(2) fastjson 的过滤器使用(SSH hibernate)以及转换对象出现 $ref

  毕业设计,用SSH框架,但是想要做出异步请求数据的效果,使用了ajax方式,其中数据传递的类型为json。ajax使用用的jQuery的ajax。其实struts里也支持ajax功能,但是我觉得用太多struts的自带标签,想使用bootstrap的定义风格就不好了,页面效果会打折扣,另外个人觉得现在的更广泛的是前端的写前端的,尽量不要前台的也需要知道struts如何写,之后重构也更麻烦。[补充,搜索过一个有个插包使在struts可以用bootstrap,但是觉得可能支持的bootstrap的版本会低于当前]

  言归正传,使用json的话需要使用工具,有java自带的JSONArray、JSONObject,但是使用的时候回提示存在类无法初始化,即指用到的类并不存在,需要引入其他的jar包,后来网上去查了下,导入了一些包,但是还是有问题,可能是没有完全。觉得麻烦就放弃了用这个。于是找了fastjson,自称是最快的,转换速度比gson(google)的快6倍。目前的项目你只有到github上去下载最新的,网上的很多以前的文章给出的链接已经失效。github地址:https://github.com/alibaba/fastjson   说明一句文档实在太过简略。

  官方的常见问题:https://github.com/alibaba/fastjson/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98

常用的方法是:

public static final String toJSONString(Object object);
public static final <T> T parseObject(String text, Class<T> clazz, Feature... features);

其实参考官方的说明,更加清楚,有中文解释,这里说一下,官方没有提到的问题。
1.过滤器的使用
  问题:
  对hibernate反向工程生成的类,若有引用其他的表的主键做外键,就会产生一个那张表的反向工程的类,这样我们对通过查询得到的记录,我是用Criteria方式查询,返回一个list对象。我们知道,hibernate查询若是采用lazy延迟方式加载(eager方式循环引用,好像容易出问题),在session关闭,再想获取未加载的数据时,会出现异常no session。不幸的是,直接使用fastjson转化对象成为json的时候就会查询未加载的对象,从而产生异常。
  解决办法:
  举个例子我们有一个专业培养计划表,里面又一个属性叫专业id,hibernate会生成一个专业类和专业培养计划类,培养计划类中有个成员对象是专业类对象,事实上我们并不需要专业类的其他属性,知道专业id即可,这是我们可以使用过滤器,使自己只对专业id进行序列化。这样就不会引起hibernate异常。
  过滤的方式,我喜欢的方式有两种,一种是配置简单过滤器,另一种是注解方式声明不序列某属性。各有优缺点,建议结合使用。
a.
配置简单过滤器
   
List<Professioncultivationplan> list = professioncultivationplanDao
                    .query(professioncultivationplan);
// JSON过滤
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(
            Profession.class, "id");
 //fastjosn的自带简单过滤器,允许配置只序列化那个属性,这里是序列化id,这样对于在Professioncultivationplan里的profession的其他属性就不会去序列化生成json字符串

result = JSON.toJSONString(list, filter,SerializerFeature.DisableCircularReferenceDetect);//注意SerializerFeature.DisableCircularReferenceDetect是标题的第二问题的解决方案

  b. 注解

    private Integer id;
    @JSONField(serialize = false)
    private Profession profession;
    private String groupcourseCode;
    private String groupcourseCategory;
    private Float credit;
    @JSONField(serialize = false)
    private Set<Coursecultivationplan> coursecultivationplans = new HashSet<Coursecultivationplan>(
            0);
//这里我对private Profession profession;private Set<Coursecultivationplan> coursecultivationplans...使用了不进行序列haunted的声明,使用注解可以不用反复使用过滤,但是明显我们也无法使用profession里的id了,若是这个id没有什么用使用注解方式是可以的,否则应该使用第一种方式,其中第一种方式只是简单的配置过滤器,还有实现一个接口,自己写一个过滤器,更加丰富是定制过滤方式,一般而言简单过滤器就可以解决了

2.转换hibernate对象出现 $ref 

  使用fastjson,当进行toJSONString的时候,默认如果重用对象的话,会使用引用的方式进行引用对象。也就是指,hibernate为我们的profession对象是在java里是同一个的话,序列化就会得到一个引用。还有一种叫循环引用——我们需要序列化的对象中存在循环引用,在许多的json库中,这会导致stackoverflow——的情况也会出现 $ref 。
  在我的毕业设计里面并没有出现循环引用(A包含B,B有包含A),但是我获取一个专业培养计划表的时候,若是有多条结果,从第二条开始就会出现profession的序列化并不是第一个的记录的一样,而是引用第一个结果,所以传递到前端页面循环显示记录的时候,从第二条开始,专业id那一列变成了undefined,或许有其他的读取这个一列的方式,但是我想生成同样的结果,会比较方便和一致性,所以需要设置增加参数
SerializerFeature.DisableCircularReferenceDetect,禁止以引用方式引用对象。
一下是未设置的转换出来的json结果:
[{"corediscipline":"计算机科学与技术change","dlsjCompulsoryratio":0,"dlsjCredit":35,"dlsjElectiveratio":0,"dlsjPeriod":0,"id":1,"profession":{"id":5},"tsjyCompulsoryratio":30.9,"tsjyCredit":69.5,"tsjyElectiveratio":10.8,"tsjyPeriod":1042,"zyjcCompulsoryratio":32.4,"zyjcCredit":63,"zyjcElectiveratio":5.4,"zyjcPeriod":945,"zykCompulsoryratio":9.6,"zykCredit":36,"zykElectiveratio":10.8,"zykPeriod":510},
{"corediscipline":"计算机科学与技术save","dlsjCompulsoryratio":0,"dlsjCredit":35,"dlsjElectiveratio":0,"dlsjPeriod":0,"id":8,"profession":{"$ref":"$[0].profession"},"tsjyCompulsoryratio":30.9,"tsjyCredit":69.5,"tsjyElectiveratio":10.8,"tsjyPeriod":1042,"zyjcCompulsoryratio":32.4,"zyjcCredit":63,"zyjcElectiveratio":5.4,"zyjcPeriod":945,"zykCompulsoryratio":9.6,"zykCredit":36,"zykElectiveratio":10.8,"zykPeriod":510}]

-------------------------------------------------------
更新时间:2016年5月6日16:18:32

内容:对过滤器的补充

在之前的fastjson的SimplePropertyPreFilter基础之上,有的人编写了一个比较好的过滤器,可以过滤多个类及属性条件。
作者出处: 晨风²º¹⁴ 2013年09月13日 于 爱Java 发表  http://aijava.cn/597.html

这里贴一下所编写的过滤器以及用法:
  1 import java.util.HashMap;
  2 import java.util.Map;
  3 
  4 import com.alibaba.fastjson.JSON;
  5 import com.alibaba.fastjson.serializer.JSONSerializer;
  6 import com.alibaba.fastjson.serializer.PropertyPreFilter;
  7 import com.alibaba.fastjson.serializer.SerializerFeature;
  8 
  9 public class ComplexPropertyPreFilter implements PropertyPreFilter {
 10 
 11     private Map<Class<?>, String[]> includes = new HashMap<>();
 12     private Map<Class<?>, String[]> excludes = new HashMap<>();
 13           
 14     public Map<Class<?>, String[]> getIncludes() {
 15         return includes;
 16     }
 17 
 18     public void setIncludes(Map<Class<?>, String[]> includes) {
 19         this.includes = includes;
 20     }
 21 
 22     public Map<Class<?>, String[]> getExcludes() {
 23         return excludes;
 24     }
 25 
 26     public void setExcludes(Map<Class<?>, String[]> excludes) {
 27         this.excludes = excludes;
 28     }
 29 
 30     static {
 31         JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
 32     }
 33           
 34     public ComplexPropertyPreFilter() {
 35               
 36     }
 37           
 38     public ComplexPropertyPreFilter(Map<Class<?>, String[]> includes) {
 39         super();
 40         this.includes = includes;
 41     }
 42     @Override
 43     public boolean apply(JSONSerializer serializer, Object source, String name) {
 44         // TODO Auto-generated method stub
 45         
 46         //对象为空。直接放行
 47         if (source == null) {
 48             return true;
 49         }
 50      // 获取当前需要序列化的对象的类对象
 51         Class<?> clazz = source.getClass();
 52      // 无需序列的对象、寻找需要过滤的对象,可以提高查找层级
 53         // 找到不需要的序列化的类型
 54         for (Map.Entry<Class<?>, String[]> item : this.excludes.entrySet()) {
 55             // isAssignableFrom(),用来判断类型间是否有继承关系
 56             if (item.getKey().isAssignableFrom(clazz)) {
 57                 String[] strs = item.getValue();
 58                       
 59                 // 该类型下 此 name 值无需序列化
 60                 if (isHave(strs, name)) {
 61                     return false;
 62                 }
 63             }
 64         }
 65               
 66         // 需要序列的对象集合为空 表示 全部需要序列化
 67         if (this.includes.isEmpty()) {
 68             return true;
 69         }
 70               
 71         // 需要序列的对象
 72         // 找到不需要的序列化的类型  改正:找到需要序列化的类型
 73         for (Map.Entry<Class<?>, String[]> item : this.includes.entrySet()) {
 74             // isAssignableFrom(),用来判断类型间是否有继承关系
 75             if (item.getKey().isAssignableFrom(clazz)) {
 76                 String[] strs = item.getValue();
 77                 // 该类型下 此 name 值无需序列化  改正:需要序列化
 78                 if (isHave(strs, name)) {
 79                     return true;
 80                 }
 81             }
 82         }
 83         
 84         
 85         
 86         return false;
 87     }
 88 
 89     /*
 90      * 此方法有两个参数,第一个是要查找的字符串数组,第二个是要查找的字符或字符串
 91      */
 92     public static boolean isHave(String[] strs, String s) {
 93               
 94         for (int i = 0; i < strs.length; i++) {
 95             // 循环查找字符串数组中的每个字符串中是否包含所有查找的内容
 96             if (strs[i].equals(s)) {
 97                 // 查找到了就返回真,不在继续查询
 98                 return true;
 99             }
100         }
101               
102         // 没找到返回false
103         return false;
104     }
105     
106     
107     
108 }

上面是原作者的过滤器,原文章里对使用,测试不够详细,这里介绍两种用法:

            List<SubgradCourseRelation> list = subgradCourseRelationDao
                    .query(subgradCourseRelation);
            // 有两个类需要设置过滤course与Subgraduationrequirement
            ComplexPropertyPreFilter filter2 = new ComplexPropertyPreFilter();
            //设置不需要序列化的类及其属性
            // filter2.setExcludes(new HashMap<Class<?>, String[]>() {
            // {
            // put(Course.class, new String[] { "coursename",
            // "totalcredit", "totalperiod", "lecture",
            // "experiment", "oncomputer", "extracurricular",
            // "evaluationmethod","coursetype" });
            // put(Subgraduationrequirement.class, new String[] {
            // "graduationrequirement", "sgrCode", "sgrContent" });
            // }
            // });
            //设置想要序列化的类及其属性
            filter2.setIncludes(new HashMap<Class<?>, String[]>() {
                {
                    put(SubgradCourseRelation.class, new String[] { "id",
                            "weight", "arrivedFirst", "arrivedSecond",
                            "arrivedThrid", "coursefeedback", "course",
                            "subgraduationrequirement" });
                    put(Course.class, new String[] { "id" });
                    put(Subgraduationrequirement.class, new String[] { "id" });
                }
            });
            /* 设置ex和includes序列化的结果都是:
             [{"arrivedFirst":0,"arrivedSecond":0,"arrivedThrid":0,"course":{"id":1},"coursefeedback":0,"id":1,"subgraduationrequirement":{"id":1},"weight":15},
             {"arrivedFirst":0,"arrivedSecond":0,"arrivedThrid":0,"course":{"id":10},"coursefeedback":0,"id":2,"subgraduationrequirement":{"id":1},"weight":15},
             {"arrivedFirst":40,"arrivedSecond":0,"arrivedThrid":0,"course":{"id":11},"coursefeedback":0,"id":3,"subgraduationrequirement":{"id":1},"weight":15},
             {"arrivedFirst":0,"arrivedSecond":0,"arrivedThrid":0,"course":{"id":85},"coursefeedback":0,"id":4,"subgraduationrequirement":{"id":1},"weight":15},
             {"arrivedFirst":0,"arrivedSecond":0,"arrivedThrid":0,"course":{"id":22},"coursefeedback":0,"id":5,"subgraduationrequirement":{"id":1},"weight":20},
             {"arrivedFirst":0,"course":{"id":30},"coursefeedback":0,"id":6,"subgraduationrequirement":{"id":1},"weight":20}] //这里存在某些值为null,所以缺项并未序列化
             * */

            result = JSON.toJSONString(list, filter2,
                    SerializerFeature.DisableCircularReferenceDetect);

 



posted @ 2016-04-23 19:32  孤鸿寄语  阅读(1587)  评论(0编辑  收藏  举报