避免对未获取的惰性对象进行Jackson序列化
我有一个简单的控制器,返回一个用户对象,这个用户有一个属性坐标具有hibernate属性FetchType.LAZY。
当我尝试获取这个用户时,我总是要加载所有的坐标来获取用户对象,否则当jackson尝试序列化用户时抛出exception:
com.fasterxml.jackson.databind.JsonMappingException:无法初始化代理 – 没有会话
这是因为jackson试图抓取这个无法取得的物体。 这里是对象:
public class User{ @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") @JsonManagedReference("user-coordinate") private List<Coordinate> coordinates; } public class Coordinate { @ManyToOne @JoinColumn(name = "user_id", nullable = false) @JsonBackReference("user-coordinate") private User user; }
和控制器:
@RequestMapping(value = "/user/{username}", method=RequestMethod.GET) public @ResponseBody User getUser(@PathVariable String username) { User user = userService.getUser(username); return user; }
有没有办法告诉jackson不要序列化未提取的对象? 我一直在寻找其他3年前发布的实现jackson-hibernate-module的答案。 但是可能用新的jacksonfunction来实现。
我的版本是:
- spring3.2.5
- hibernate4.1.7
- jackson2.2
提前致谢。
我终于find了解决scheme! 感谢indybee给我一个线索。
Spring 3.1,Hibernate 4和Jackson-Module-Hibernate教程为Spring 3.1和更早版本提供了一个很好的解决scheme。 但是从版本3.1.2开始,Spring拥有自己的MappingJackson2HttpMessageConverter ,其function几乎与本教程中的一样,所以我们不需要创build这个自定义的HTTPMessageConverter。
使用javaconfig我们不需要创buildHibernateAwareObjectMapper ,我们只需要将Hibernate4Module添加到Spring已经具有的默认MappingJackson2HttpMessageConverter中 ,并将其添加到应用程序的HttpMessageConverters中,所以我们需要:
-
从WebMvcConfigurerAdapter扩展我们的springconfiguration类,并覆盖方法configureMessageConverters 。
-
在该方法中,添加MappingJackson2HttpMessageConverter与在previus方法中注册的Hibernate4Module 。
我们的configuration类应该是这样的:
@Configuration @EnableWebMvc public class MyConfigClass extends WebMvcConfigurerAdapter{ //More configuration.... /* Here we register the Hibernate4Module into an ObjectMapper, then set this custom-configured ObjectMapper * to the MessageConverter and return it to be added to the HttpMessageConverters of our application*/ public MappingJackson2HttpMessageConverter jacksonMessageConverter(){ MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); ObjectMapper mapper = new ObjectMapper(); //Registering Hibernate4Module to support lazy objects mapper.registerModule(new Hibernate4Module()); messageConverter.setObjectMapper(mapper); return messageConverter; } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //Here we add our custom-configured HttpMessageConverter converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } //More configuration.... }
如果你有一个xmlconfiguration,你不需要创build你自己的MappingJackson2HttpMessageConverter,但你需要创build出现在本教程(HibernateAwareObjectMapper)中的个性化映射器,所以你的xmlconfiguration应该如下所示:
<mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.pastelstudios.json.HibernateAwareObjectMapper" /> </property> </bean> </mvc:message-converters>
希望这个答案是可以理解的,并帮助有人find解决这个问题,任何问题随时问!
这与@rick接受的解决scheme类似。
如果您不想触摸现有的消息转换器configuration,则可以声明一个Jackson2ObjectMapperBuilder
bean,如下所示:
@Bean public Jackson2ObjectMapperBuilder configureObjectMapper() { return new Jackson2ObjectMapperBuilder() .modulesToInstall(Hibernate4Module.class); }
不要忘记添加以下依赖到您的Gradle文件(或Maven):
compile 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:2.4.4'
如果你有一个spring-boot应用程序,并且想要保持从application.properties
文件修改Jacksonfunction的能力,那么这很有用。
从Spring 4.2开始,使用Spring Boot和javaconfig,注册Hibernate4Module现在就像添加到你的configuration一样简单:
@Bean public Module datatypeHibernateModule() { return new Hibernate4Module(); }
ref: https : //spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
如果您使用XMLconfiguration并使用<annotation-driven />
,我发现您必须按照Jackson Github帐户中的build议在<annotation-driven>
内部嵌套<message-converters>
。
像这样:
<annotation-driven> <message-converters> <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <beans:property name="objectMapper"> <beans:bean class="com.pastelstudios.json.HibernateAwareObjectMapper" /> </beans:property> </beans:bean> </message-converters> </annotation-driven>
`
在Spring Data Rest的情况下,当@ r1ckr发布的解决scheme工作的时候,所需要的就是根据你的Hibernate版本添加下列依赖项之一:
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate4</artifactId> <version>${jackson.version}</version> </dependency>
要么
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId> <version>${jackson.version}</version> </dependency>
在Spring Data Rest中有一个类:
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper
它将在应用程序启动时自动检测和注册模块。
然而有一个问题:
发行序列化Lazy @ManyToOne
对于那些来这里寻找解决scheme的基于Apache CXF的RESTful服务的人来说,修复它的configuration如下:
<jaxrs:providers> <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider"> <property name="mapper" ref="objectMapper"/> </bean> </jaxrs:providers> <bean id="objectMapper" class="path.to.your.HibernateAwareObjectMapper"/>
HibernateAwareObjectMapper被定义为:
public class HibernateAwareObjectMapper extends ObjectMapper { public HibernateAwareObjectMapper() { registerModule(new Hibernate5Module()); } }
截至2016年6月,需要以下依赖项(假设您使用Hibernate5):
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId> <version>2.7.4</version> </dependency>
您可以使用Spring Data Rest项目中的以下帮助程序:
Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
我试了@ rick的有用答案 ,但遇到了像“ jackson-datatype-jsr310 ”这样的“众所周知的模块”不会自动注册的问题,尽pipe它们在类path中。 (这篇博文解释了自动注册。)
扩展@ rick的答案,这里是使用Spring的Jackson2ObjectMapperBuilder创buildObjectMapper
。 这会自动注册“众所周知的模块”,并设置除安装Hibernate4Module
之外的特定function。
@Configuration @EnableWebMvc public class MyWebConfig extends WebMvcConfigurerAdapter { // get a configured Hibernate4Module // here as an example with a disabled USE_TRANSIENT_ANNOTATION feature private Hibernate4Module hibernate4Module() { return new Hibernate4Module().disable(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION); } // create the ObjectMapper with Spring's Jackson2ObjectMapperBuilder // and passing the hibernate4Module to modulesToInstall() private MappingJackson2HttpMessageConverter jacksonMessageConverter(){ Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .modulesToInstall(hibernate4Module()); return new MappingJackson2HttpMessageConverter(builder.build()); } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } }
虽然这个问题与这个问题略有不同: 在序列化Hibernate对象时引发了奇怪的Jacksonexception ,但是可以用相同的方式修复底层问题:
@Provider public class MyJacksonJsonProvider extends JacksonJsonProvider { public MyJacksonJsonProvider() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); setMapper(mapper); } }
我试过了,工作:
//延迟加载的自定义configuration
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> { @Override public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeNull(); } }
并configuration映射器:
mapper = new JacksonMapper(); SimpleModule simpleModule = new SimpleModule( "SimpleModule", new Version(1,0,0,null) ); simpleModule.addSerializer( JavassistLazyInitializer.class, new HibernateLazyInitializerSerializer() ); mapper.registerModule(simpleModule);