zoukankan      html  css  js  c++  java
  • 毕设问题(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);


    欢迎转载,但请声明出处。
  • 相关阅读:
    Oracle9使用oradata恢复数据库
    我该怎么安排下属的工作项目经理如何分配任务
    如果说中国的程序员技术偏低,原因可能在这里
    项目经理问:为什么总是只有我在加班 – 挂包袱现象
    【转】面试真经
    [JAVA]PING和TELNET用法介绍
    Hello World 你懂的
    线程间操作控件
    获取客户端相关信息
    winfrom 特效 [转载]
  • 原文地址:https://www.cnblogs.com/tianjiqx/p/5425353.html
Copyright © 2011-2022 走看看