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);


    欢迎转载,但请声明出处。
  • 相关阅读:
    SQL Server Audit监控触发器状态
    SQL Server 数据变更时间戳(timestamp)在复制中的运用
    SQL Server 更改跟踪(Chang Tracking)监控表数据
    SQL Server 变更数据捕获(CDC)监控表数据
    SQL Server 事件通知(Event notifications)
    SQL Server 堆表行存储大小(Record Size)
    SQL Server DDL触发器运用
    SQL Server 默认跟踪(Default Trace)
    SQL Server 创建数据库邮件
    SQL Server 跨网段(跨机房)FTP复制
  • 原文地址:https://www.cnblogs.com/tianjiqx/p/5425353.html
Copyright © 2011-2022 走看看