单向关联中解决jackson序列化对象时关联对象为空,无法序列化成json的问题

      在我们用hibernate做查询数据的时候,我们会遇到一些问题,就是我们后台向前端以json返回数据的时候,会遇到返回对象中的关联对象属性为空,无法进行序列化;这是因为后台序列化对象的时候位于我们的第一次查询和关联数据查询之间。就是说在关联对象的查询还没进行的时候就开始进行jackson的序列化了,而这时我们的关联对象还没有查询出来数据进行赋值,所以会出现这个问题,所以有以下两种解决方案,但是有个方案有缺陷,就是对于单向关系有效,对于双向关系会出现循环序列化。

      为什么出现这样的问题呢,对于双向关系无效,因为这次操作只解决了如果序列化对象的关联对象为空就不进行序列化;虽然我们解决了空引用的问题,但是新问题又出现了,这是必然的。我们可以想象一下,如果是双向关系,我们查询成功了,那么必然双方都含有对方,那么你在序列化对方的时候,对方序列化你,就会陷入无限的循环序列化,死在里面。

 

终极方案:

       有一个办法可以解决这种问题,就是用@JsonIgnoreProperties这个注解去忽略对象上的关联属性,那么这个属性就不会参与序列化和反序列化。然后我们就可以根据查询出来的数据,手动的对关联对象进行装填,然后返回。因为这个方案只是在序列化的时候忽略关联对象,但是不影响我们的懒加载,我们就可以把主对象装进一个map,然后把它的关联对象数据也装进map进行返回,这是个办法。并且这个方法还能够直接解决我们之前的关联对象的引用为空的问题,一举两得。为什么?因为已经忽略了关联对象,也就不存在空的问题了,所以,在关联数据查询出来之后要把它们分开装进一个map的道理,这样,也就能够序列成功了,关联对象里面的关联对象全部都要忽略才行哦,因为它也需要引用啊,道理是相同的,无非就是把关联数据单独拿出来装填罢了。

      比如说:我有个TeacherBean对象,有个StudnetBean对象,老师含有学生,学生含有老师,明显一对多的双向关系,那么我们把老师中的学生关联属性忽略,把学生中老师的关联属性忽略,用@JsonIgnoreProperties这个注解。那么我们通过查询老师必然把学生也查出来了,因为不影响懒加载。所以我们可以分别装map.put("teacher",teacherBean),map.put("studnts",teacherBean.students),然后返回。

   

有缺陷方案:

下面还是说一下提到的第一种方式(不能解决双向关联循环序列化,上面说了,下面是解决不能序列化成功的问题,而不是成功之后循环序列化的问题):

 

前情提要:解决这个关联对象为空的问题方法就是:

1、自己写继承ObjectMapper的CustomMapper类(类名无所谓,反正是自己定义)

2、向spring-mvc.xml配置文件中注册CustomMapper

3、在前端控制器去使用这个CustomMapper来替代我们之前使用的ObjectMapper进行对象序列化成json的类(使用方式和使用jackson的ObjectMapper类是一样的)

 

自定义的ObjectMapper和如何在spring-mvc中xml注册呢,链接:https://blog.csdn.net/IT_CREATE/article/details/86592388

找到:spring-mvc.xml配置(配置的是表现层的东西)   这段即可(CustomMapper的创建和注册都有

 

 

举例:查询用户信息的时候(用户中含有地址和其他信息两个关联对象类)

用户对象:(get、set方法就不贴出来了)UserBean:

@JsonFilter("userFilter")就是将利用Jackson的JsonFilter来实现动态过滤数据列,后面会用到这个过滤器,但这不是这次主要的

利用Jackson的JsonFilter来实现动态过滤数据列(数据列权限控制):http://www.cnblogs.com/tomcatandjerry/p/9139976.html

@Entity
@Table(name="t_user")
@OptimisticLocking(type=OptimisticLockType.VERSION)
@JsonFilter("userFilter")
public class UserBean implements Serializable{

	private static final long serialVersionUID = -1879857877221009050L;

	@Id
	@Column(name="id")
	@GenericGenerator(name="hibernate.id",strategy="identity")
	@GeneratedValue(generator="hibernate.id")
	private Integer id;
	
	@Column(name="login_name",length=20)
	private String loginName;
	
	@Column(name="user_name",length=20)
	private String userName;
	
	@Column(name="user_pwd",length=32)
	private String password;
	
	@Column(name="age")
	private Integer age;
	
	@Column(name="gender")
	private Integer gender;
	
	@Column(name="birthday")
	private Date birthday;
	
	@Column(name="create_time")
	private Date createTime;
	
	@Version
	private Integer version;
	
	/**
	 * 人拥有人员信息,单向关联
	 */
	@OneToOne(fetch=FetchType.LAZY)
	@Cascade(value= {CascadeType.ALL})
	@JoinColumn(name="fk_info_id")
	private UserInfoBean userInfo;
	/**
	 * 人拥有多个地址,单向关联
	 */
	
	@OneToMany(fetch=FetchType.LAZY)
	@Cascade(value= {CascadeType.ALL})
	@JoinColumn(name="fk_user_id")
	private Set<AddressBean> adds;
}

用户信息类 UserInfoBean:

@Entity
@Table(name="t_user_info")
public class UserInfoBean implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7547766667713260207L;
	
	@Id
	@Column(name="id")
	@GenericGenerator(name="hibernate.id",strategy="identity")
	@GeneratedValue(generator="hibernate.id")
	private Integer id;
	
	@Column(name="idcard",length=18)
	private String idcard;
	
	@Column(name="job",length=20)
	private String job;
}

用户地址类 AddressBean:

@Entity
@Table(name="t_address")
public class AddressBean implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 876189137233337215L;
	
	@Id
	@Column(name="id")
	@GenericGenerator(name="hibernate.id",strategy="identity")
	@GeneratedValue(generator="hibernate.id")
	private Integer id;
	
	@Column(name="address",length=120)
	private String address;
	
	@Column(name="telphone",length=13)
	private String telphone;
}

 

前端页面 js:

$('#btn01').bind("click", function(){
	var arrs = new Array("userInfo","adds");
	var data = {"id":26,"arrs":arrs};
		
	//Ajax在通过POST在传递  "数组"时,需要指定traditional:true
	//其目的是为了告知JS在序列化参数时,直接将arrs作为参数名称,而不是将arrs[]来作为参数名称
	//不做深度序列化
	console.log(data);
	var url = "users/getById";
	$.ajax({
		type: "POST",
		url: url,
		contentType:"application/x-www-form-urlencoded",
		traditional:true,
		data: data,
		success: function(msg){
			console.log(msg);
		}
	});
});

        从js中可以看出,我们要查询id为26的用户信息。上面向后台传的arrs指的是你不需要用户UserBean中的哪些字段。

        起到忽略这些字段作用的也就是我们在对象上添加了@JsonFilter("userFilter")起到的作用

 

后台controller代码:

/**
 * 
 * @param id
 * @param arrs 代表在使用Jackson序列化对象时,需要排除属性
 * @param out
 * @param res
 */
@RequestMapping(value = "/getById")
public void getUserBeanById(int id,String[] arrs,PrintWriter out, HttpServletResponse res) {
	UserBean user = null;
	try {
		user = userServiceImpl.getUserBeanById(id);
		//通过自己定义的ObjectMapper解决 页面需要关联对象时,做延迟加载的问题
		CustomMapper om = new CustomMapper();
			
		if(arrs != null && arrs.length > 0) {
			om.setFilterProvider(new SimpleFilterProvider().addFilter("userFilter",
					SimpleBeanPropertyFilter.serializeAllExcept(arrs)));
                //serializeAllExcept 表示序列化全部,除了指定字段
                //filterOutAllExcept 表示过滤掉全部,除了指定的字段
		}else {
			om.setFilterProvider(new SimpleFilterProvider().addFilter("userFilter",
					SimpleBeanPropertyFilter.serializeAll()));
		}
			
		String json = om.writeValueAsString(user);
		res.setContentType("application/json;charset=utf-8");
		out.print(json);
	} catch (Exception e) {
		// TODO: handle exception
		log.error("UserController-------getUserBeanById()", e);
	}

}

      通过前端传下来的值手动的指定你需要哪些数据,在用户对象上定义的@JsonFilter("userFilter")过滤器只是解决我们想要哪些属性的问题,并没有解决在序列化过程中出现关联对象为空的情况。假设我们没有传下来arrs,也就是我们没有想要过滤的字段,我们想把关联信息一起序列化,那么这里就用到了通过自己定义的ObjectMapper解决 页面需要关联对象时,做延迟加载的问题(也就是上面代码中的CustomMapper)

      前面已经提到了,在对象进行jackson需序列化的时候,做一次查询就要序列化一次,所以在还没有进行延迟加载的第二次查询的时候就开始进行了第一次序列化,这回导致关联对象为空的问题。

      比如这里先进行用户对象的查询,这是第一次查询,然后序列化,但是用户对象中包含的地址类和用户信息类还没有开始进行懒加载操作,也就是第二次查询,所以用户对象中包含的地址对象和用户信息对象的引用都为null,所以就无法序列化成功,会报错。

      因为我们用的是jackson的ObjectMapper来进行序列化成json的,所以我们需要在spring-mvc的配置文件中去注册我们自己的ObjectMapper,也就是我们自己写的继承了ObjectMapper的CustomMapper类。

      自定义的ObjectMapper和如何在spring-mvc中xml注册呢,链接:https://blog.csdn.net/IT_CREATE/article/details/86592388

找到:spring-mvc.xml配置(配置的是表现层的东西)   这段即可(CustomMapper的创建和注册都有

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值