毕设问题(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);
欢迎转载,但请声明出处。