У меня есть простой контроллер, который возвращает объект User, у этого пользователя есть координаты атрибута, у которых есть свойство hibernate FetchType.LAZY.
Когда я пытаюсь получить этого пользователя, мне всегда нужно загружать все координаты, чтобы получить объект пользователя, иначе, когда Джексон попытается сериализовать пользователя, выдается исключение:
com.fasterxml.jackson.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. Но, вероятно, этого можно было бы достичь с помощью новой функции Джексона.
Мои версии:
- Весна 3.2.5
- Гибернация 4.1.7
- Джексон 2.2
Заранее спасибо.
Ответы:
Наконец-то я нашел решение! Спасибо Indybee за то, что дал мне подсказку.
Учебник Spring 3.1, Hibernate 4 и Jackson-Module-Hibernate предлагает хорошее решение для Spring 3.1 и более ранних версий. Но начиная с версии 3.1.2 Spring имеет собственный MappingJackson2HttpMessageConverter с почти той же функциональностью, что и в руководстве, поэтому нам не нужно создавать этот настраиваемый HTTPMessageConverter.
С javaconfig нам также не нужно создавать HibernateAwareObjectMapper , нам просто нужно добавить Hibernate4Module к MappingJackson2HttpMessageConverter по умолчанию, который уже есть в Spring, и добавить его в HttpMessageConverters приложения, поэтому нам нужно:
Расширьте наш класс конфигурации Spring из WebMvcConfigurerAdapter и переопределите метод configureMessageConverters .
В этом методе добавьте MappingJackson2HttpMessageConverter с Hibernate4Module, зарегистрированным в предыдущем методе.
Наш класс конфигурации должен выглядеть так:
@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, но вам нужно создать персонализированный сопоставитель, который отображается в руководстве (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(); }
ссылка: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
источник
Hibernate5Module
. Кроме того, требуется зависимость от<groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-hibernate5</artifactId>
Это похоже на принятое решение @rick.
Если вы не хотите трогать существующую конфигурацию конвертеров сообщений, вы можете просто объявить
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'
Полезно, если у вас есть пружинный сапогприложение, и вы хотите сохранить возможность изменять функции Джексона из
application.properties
файла.источник
application.properties
файла?Hibernate4Module
is legacyВ случае Spring Data Rest, хотя решение, опубликованное @ r1ckr, работает, все, что требуется, - это добавить одну из следующих зависимостей в зависимости от вашей версии Hibernate:
или же
В Spring Data Rest есть класс:
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper
который автоматически обнаружит и зарегистрирует модуль при запуске приложения.
Однако есть проблема:
Проблема с сериализацией ленивого @ManyToOne
источник
@Bean hibernate5Module()
Если вы используете XML-конфигурацию и используете
<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>
`
источник
Для тех, кто пришел сюда, чтобы найти решение для службы RESTful на основе Apache CXF, конфигурация, которая его исправляет, приведена ниже:
<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 4.3 (без загрузки) и Hibernate 5.1, в котором мы перевели все типы fetchtypes
fetch=FetchType.LAZY
из вариантов изfetch=FetchType.EAGER
соображений производительности. Сразу мы увиделиcom.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy
исключение из-за проблемы с отложенной загрузкой.Сначала мы добавляем следующую зависимость maven:
Затем в наш файл конфигурации Java MVC добавляется следующее:
@Configuration @EnableWebMvc public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { Hibernate5Module h5module = new Hibernate5Module(); h5module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION); h5module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING); for (HttpMessageConverter<?> mc : converters){ if (mc instanceof MappingJackson2HttpMessageConverter || mc instanceof MappingJackson2XmlHttpMessageConverter) { ((AbstractJackson2HttpMessageConverter) mc).getObjectMapper().registerModule(h5module); } } return; }
Ноты:
Вам необходимо создать и настроить Hibernate5Module, чтобы получить поведение, подобное Джексону, без этого модуля. По умолчанию сделаны несовместимые предположения.
У нас
WebMvcConfigurerAdapter
много другой конфигурации, и мы хотели избежать другого класса конфигурации, поэтому мы не использовалиWebMvcConfigurationSupport#addDefaultHttpMessageConverters
функцию, о которой говорилось в других сообщениях.WebMvcConfigurerAdapter#configureMessageConverters
отключает всю внутреннюю конфигурацию преобразователей сообщений Spring. Мы предпочли избежать потенциальных проблем, связанных с этим.Использование
extendMessageConverters
разрешенного доступа ко всем автоматически настраиваемым классам Джексона без потери конфигурации всех других конвертеров сообщений.С его помощью
getObjectMapper#registerModule
мы смогли добавитьHibernate5Module
к существующим конвертерам.Это дополнение решило проблему с гибернацией и отложенной загрузкой, но вызвало остаточную проблему со сгенерированным форматом JSON . Как сообщается в этой проблеме github , модуль отложенной загрузки hibernate-jackson в настоящее время игнорирует аннотацию @JsonUnwrapped, что приводит к потенциальным ошибкам данных. Это происходит независимо от настройки функции принудительной загрузки. Проблема существует с 2016 года.
Запись
Похоже, что, добавив следующее к классам, которые загружаются лениво, встроенный ObjectMapper работает без добавления модуля hibernate5:
@JsonIgnoreProperties( {"handler","hibernateLazyInitializer"} ) public class Anyclass {
источник
kotlin @Bean fun hibernate5Module(): Module = Hibernate5Module()
Вы можете использовать следующий помощник из проекта Spring Data Rest:
источник
Я сделал очень простое решение этой проблемы.
@JsonInclude(JsonInclude.Include.NON_EMPTY) public Set<Pendency> getPendencies() { return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>(); }
В моем случае я выдавал ошибку, потому что всякий раз, когда я возвращал Pendencies, в качестве хорошей практики я преобразовал его в список, который нельзя изменить, но как он мог или не мог быть ленивым в зависимости от метода, который я использовал для получения экземпляра (с выборкой или без нее), я провожу тест до того, как он был инициализирован Hibernate, и добавляю аннотацию, которая предотвращает сериализацию пустого свойства и решает мою проблему.
источник
Я целый день пытался решить ту же проблему. Вы можете сделать это, не меняя существующую конфигурацию конвертеров сообщений.
На мой взгляд, самый простой способ решить эту проблему всего за 2 шага с помощью jackson-datatype-hibernate :
build.gradle.kts
:implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
@Bean
@Bean fun hibernate5Module(): Module = Hibernate5Module()
Обратите внимание, что
Module
этоcom.fasterxml.jackson.databind.Module
неjava.util.Module
Также хорошей практикой является добавление
@JsonBackReference
&@JsonManagedReference
к отношениям@OneToMany
&@ManyToOne
.@JsonBackReference
может быть только 1 в классе.источник
Хотя этот вопрос немного отличается от этого: при сериализации объекта 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 = new JacksonMapper(); SimpleModule simpleModule = new SimpleModule( "SimpleModule", new Version(1,0,0,null) ); simpleModule.addSerializer( JavassistLazyInitializer.class, new HibernateLazyInitializerSerializer() ); mapper.registerModule(simpleModule);
источник
Другое решение для весенней загрузки: конфигурация:
spring.jpa.open-in-view=true
источник
Я попробовал полезный ответ @rick , но столкнулся с проблемой, что «хорошо известные модули», такие как jackson-datatype-jsr310, не были автоматически зарегистрированы, несмотря на то, что они были в пути к классам. (Это сообщение в блоге объясняет автоматическую регистрацию.)
Расширяя ответ @ rick, вот вариант с использованием Spring 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() .modulesToInstall(hibernate4Module()); return new MappingJackson2HttpMessageConverter(builder.build()); } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } }
источник
hibernate5Module
(потому что я использую hibernate 5), Джексон игнорирует прокси неинициализированных объектов, но кажется, что «известные модули» не регистрируются, несмотря на то, что я использую ваш подход. Я знаю это, потому что некоторые части моего приложения сломаны, и когда я удаляю модуль, они снова работают.DelegatingWebMvcConfiguration
класс (вместоWebMvcConfigurerAdapter
), предложенный Jackson-Antpath-Filter :@Override public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) { messageConverters.add(jacksonMessageConverter()); addDefaultHttpMessageConverters(messageConverters); }
Hibernate4Module
это наследство. 2SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
- не о вопросе