Избегайте сериализации Джексона на не извлеченных ленивых объектах
у меня есть простой контроллер, который возвращает объект пользователя, у этого пользователя есть координаты атрибута, которые имеют свойство hibernate FetchType.ЛЕНИВЫЙ.
когда я пытаюсь получить этого пользователя, мне всегда нужно загрузить все координаты, чтобы получить объект пользователя, иначе, когда Джексон пытается сериализовать пользователя, он выдает исключение:
com.быстрее!Джексон.databind.JsonMappingException: не удалось инициализировать прокси-нет сеанса
Это связано с Джексон пытается достать этот незамеченный предмет. Вот эти объекты:
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;
}
есть способ сказать Джексону, чтобы он не сериализовал несвязанные объекты? Я искал другие ответы, опубликованные 3 года назад, реализуя jackson-hibernate-module. Но, вероятно, это может быть достигнуто с помощью новой функции Джексон.
мои версии:
- Весна 3.2.5
- Hibernate 4.1.7
- Джексон 2.2
спасибо заранее.
10 ответов:
Я, наконец, нашел решение! спасибо индайби за подсказку.
учебник Spring 3.1, Hibernate 4 и Jackson-Module-Hibernate есть хорошее решение для весны 3.1 и более ранних версий. Но начиная с версии 3.1.2 весна имеет свой собственный MappingJackson2HttpMessageConverter С почти такой же функциональностью, как и в учебнике, поэтому нам не нужно создавать этот пользовательский HTTPMessageConverter.
С javaconfig мы этого не делаем нужно создать HibernateAwareObjectMapper тоже, нам просто нужно добавить Hibernate4Module по умолчанию MappingJackson2HttpMessageConverter что весна уже есть и добавить его в HttpMessageConverters приложения, так что нам нужно:
расширьте наш класс конфигурации spring от WebMvcConfigurerAdapter и переопределить метод configureMessageConverters.
на этот метод добавить MappingJackson2HttpMessageConverter С Hibernate4Module зарегистрирован в методе previus.
наш класс config должен выглядеть так:
@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.... }Если у вас есть конфигурация xml, вам не нужно создавать свой собственный MappingJackson2HttpMessageConverter либо, но вам нужно создать персонализированный mapper, который появляется в учебнике (HibernateAwareObjectMapper), поэтому ваш XML-конфиг должен выглядеть так:
<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>надеюсь, что этот ответ будет понятен и поможет кому-то найти решение этой проблемы, любые вопросы не стесняйтесь задавать!
начиная с Spring 4.2 и используя Spring Boot и javaconfig, Регистрация Hibernate4Module теперь так же просто, как добавление этого в вашу конфигурацию:
@Bean public Module datatypeHibernateModule() { return new Hibernate4Module(); }ref: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
это похоже на принятое решение @rick.
если вы не хотите касаться существующей конфигурации конвертеров сообщений, вы можете просто объявить
Jackson2ObjectMapperBuilderфасоль как:@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 приложение, и вы хотите сохранить возможность изменять функции Джексона от .
в случае весеннего отдыха данных, в то время как решение, опубликованное @r1ckr, работает, все, что требуется, это добавить одну из следующих зависимостей в зависимости от вашей версии 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>в рамках весеннего отдыха данных есть класс:
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelperкоторый будет автоматически обнаруживать и регистрировать модуль при запуске приложения.
однако есть проблема:
если вы используете XML config и используете
<annotation-driven />, Я обнаружил, что вы должны гнездиться<message-converters>внутри<annotation-driven>на аккаунт Jackson Github.вот так:
<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>'
для тех, кто пришел сюда, чтобы найти решение для Apache CXF на основе RESTful service, конфигурация, которая исправляет это ниже:
<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 года (при условии, что вы используете 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);
пробовал @Рик, но столкнулся с проблемой, что "известные модули", такие как jackson-datatype-jsr310 не были автоматически зарегистрированы, несмотря на то, что они находятся на пути к классам. (Это блоге объясняет автоматическую регистрацию.)
расширяя ответ @rick, вот вариант с использованием Spring's Jackson2ObjectMapperBuilder создать
ObjectMapper. Это автоматически регистрирует "известные модули" и устанавливает определенные функции в дополнение к установкеHibernate4Module.@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, основная проблема может быть исправлена таким же образом с помощью этого кода:
@Provider public class MyJacksonJsonProvider extends JacksonJsonProvider { public MyJacksonJsonProvider() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); setMapper(mapper); } }
я попробовал это, и это сработало:
/ / пользовательская конфигурация для ленивой загрузки
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> { @Override public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeNull(); } }и настроить mapper:
mapper = new JacksonMapper(); SimpleModule simpleModule = new SimpleModule( "SimpleModule", new Version(1,0,0,null) ); simpleModule.addSerializer( JavassistLazyInitializer.class, new HibernateLazyInitializerSerializer() ); mapper.registerModule(simpleModule);
Comments