Spring3.1, Hibernate4与Jackson2处理Json序列化异常

在Spring MVC + Hibernate开发项目的过程中,使用Json序列化Hibernate懒加载对象时会抛出org.hibernate.LazyInitializationException异常。原因是懒加载的对象是一个代理对象,并不是我们真正想要得到的对象,所以Json无法序列化。网上给出几种解决办法:

1使用OpenSessionInViewFilter,但经过本人多次测试未能解决(也许是我使用Hibernate4的原因,未证实)。

2. 在懒加载的对象上使用@JsonIgnore注解这样在Json序列化的时候就会忽略懒加载的对象。这样一来报错的问题确实解决了,但我想在客户端拿到被@JsonIgnore注解的真正实体对象(不是懒加载的代理对象)的时候却没办法拿到了。因为使用了@JsonIgnore注解的对象,在Json序列化的时候,它不管你是代理对象还是真正的实体对象,它一概忽略掉。这样也不是我想要的结果。

3. 使用立即加载策略,这样就不存在懒加载了,自然不会报错。但这样就失去了懒加载的意义了。

4. Spring MVC + Hibernate3可以使用jackson-module-hibernate解决Json序列化异常(自行baidu、google)。


jackson-module-hibernate不再支持Hibernate4代理对象Json序列化,替代它的是Jackson2版本。更为可笑的是Spring3.1中的MappingJacksonHttpMessageConverter(Spring中的Json转换器)使用的Jackson1.x的版本。

在此给出Spring3.1, Hibernate4与Jackson2版本的解决方案:

首先下载Jackson2需要的jar包:


1. 使用Jackson2创建一个新的MappingJacksonHttpMessageConverter

package com.zdksii.pms.common.hibernate;

import java.io.IOException;
import java.nio.charset.Charset;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.Assert;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * 重写org.springframework.http.converter.json.MappingJacksonHttpMessageConverter, 以便支持Jackson 2
 * @author xie linming
 * @date 2014年12月26日
 */
public class MappingJackson2HttpMessageConverter extends AbstractHttpMessageConverter<Object> {
	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
	private ObjectMapper objectMapper = new ObjectMapper();
	private boolean prefixJson = false;
	public MappingJackson2HttpMessageConverter() {
		super(new MediaType("application", "json", DEFAULT_CHARSET));
	}
	public void setObjectMapper(ObjectMapper objectMapper) {
		Assert.notNull(objectMapper, "ObjectMapper must not be null");
		this.objectMapper = objectMapper;
	}
	public ObjectMapper getObjectMapper() {
		return this.objectMapper;
	}
	public void setPrefixJson(boolean prefixJson) {
		this.prefixJson = prefixJson;
	}
	public boolean canRead(Class<?> clazz, MediaType mediaType) {
		JavaType javaType = getJavaType(clazz);
		return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType));
	}
	public boolean canWrite(Class<?> clazz, MediaType mediaType) {
		return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType));
	}
	protected boolean supports(Class<?> clazz) {
		// should not be called, since we override canRead/Write instead
		throw new UnsupportedOperationException();
	}
	protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
		JavaType javaType = getJavaType(clazz);
		try {
			return this.objectMapper.readValue(inputMessage.getBody(), javaType);
		} catch (JsonProcessingException ex) {
			throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
		}
	}
	protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
		JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
		@SuppressWarnings("deprecation")
		JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
		try {
			if (this.prefixJson) {
				jsonGenerator.writeRaw("{} && ");
			}
			this.objectMapper.writeValue(jsonGenerator, object);
		} catch (JsonProcessingException ex) {
			throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
		}
	}
	protected JavaType getJavaType(Class<?> clazz) {
		return TypeFactory.defaultInstance().constructType(clazz);
	}
	protected JsonEncoding getJsonEncoding(MediaType contentType) {
		if (contentType != null && contentType.getCharSet() != null) {
			Charset charset = contentType.getCharSet();
			for (JsonEncoding encoding : JsonEncoding.values()) {
				if (charset.name().equals(encoding.getJavaName())) {
					return encoding;
				}
			}
		}
		return JsonEncoding.UTF8;
	}
}

2. 创建一个HibernateAwareObjectMapper类,向ObjectMapper注册Hibernate4Module

package com.zdksii.pms.common.hibernate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module;

/**
 * 向ObjectMapper注册Hibernate4Module, Hibernate4使用Jackson 2, Hibernate3使用jackson-module-hibernate
 * @author xie linming
 * @date 2014年12月26日
 */
public class HibernateAwareObjectMapper extends ObjectMapper {
	private static final long serialVersionUID = 7958167810745447350L;
	public HibernateAwareObjectMapper() {
		Hibernate4Module hm = new Hibernate4Module();
		registerModule(hm);
	}
}


3. 将 MappingJackson2HttpMessageConverter加入到Spring容器中
<mvc:annotation-driven>
	<mvc:message-converters>
	        <bean class="com.zdksii.pms.common.hibernate.MappingJackson2HttpMessageConverter">
	            <property name="objectMapper">
	                <bean class="com.zdksii.pms.common.hibernate.HibernateAwareObjectMapper" />
	            </property>
	        </bean>
	</mvc:message-converters>
</mvc:annotation-driven>

这样就可以使用诸如@ResponseBody注解来序列化Hibernate代理对象了。如果Json序列化的是一个Hibernate代理对象,客户端得到的是null值。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值