Избегайте сериализации jacksonа на непривлекательных ленивых объектах
У меня есть простой controller, который возвращает объект User, этот пользователь имеет координаты атрибутов, которые имеют свойство hibernate FetchType.LAZY.
Когда я пытаюсь получить этого пользователя, мне всегда приходится загружать все координаты, чтобы получить объект пользователя, иначе, когда jackson попытается выполнить сериализацию, пользователь выдает исключение:
com.fasterxml.jackson.databind.JsonMappingException: невозможно инициализировать прокси – нет сеанса
Это происходит из-за того, что jackson пытается получить этот необработанный объект. Вот объекты:
public class User{ @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") @JsonManagedReference("user-coordinate") private List coordinates; } public class Coordinate { @ManyToOne @JoinColumn(name = "user_id", nullable = false) @JsonBackReference("user-coordinate") private User user; }
И controller:
@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. Но, вероятно, это может быть достигнуто с помощью новой функции jackson.
Мои версии:
- Весна 3.2.5
- Спящий режим 4.1.7
- jackson 2.2
Заранее спасибо.
- Нужно сбросить значение последовательности в Oracle
- Правильный способ написать JSON deserializer весной или расширить его
- Как я могу вставить значение свойства в Spring Bean, который был настроен с помощью аннотаций?
- Как реализованы хранилища Spring Data?
- динамически изменять источник данных Spring
- jackson 2.0 с весной 3.1
- Перенаправление на внешний URL-адрес из действия controllerа в Spring MVC
- несколько пакетов в контексте: компонентное сканирование, настройка пружины
Я наконец нашел решение! благодаря indybee за то, что дал мне ключ.
Учебник Spring 3.1, Hibernate 4 и Jackson-Module-Hibernate имеют хорошее решение для Spring 3.1 и более ранних версий. Но поскольку версия 3.1.2 Spring имеет свой собственный MappingJackson2HttpMessageConverter с почти теми же функциями, что и в учебнике, поэтому нам не нужно создавать этот настраиваемый HTTPMessageConverter.
С javaconfig нам также не нужно создавать HibernateAwareObjectMapper , нам просто нужно добавить Hibernate4Module к MappingJackson2HttpMessageConverter по умолчанию, который уже имеется Spring, и добавить его в HttpMessageConverters приложения, поэтому нам необходимо:
-
Расширьте наш весенний конфигурационный class из WebMvcConfigurerAdapter и переопределите метод configureMessageConverters .
-
По этому методу добавьте MappingJackson2HttpMessageConverter с Hibernate4Module, зарегистрированным в методе previus.
Наш class конфигурации должен выглядеть так:
@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> converters) { //Here we add our custom-configured HttpMessageConverter converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } //More configuration.... }
Если у вас есть xml-конфигурация, вам не нужно создавать свой собственный MappingJackson2HttpMessageConverter, но вам нужно создать персонализированный картограф, который появляется в учебнике (HibernateAwareObjectMapper), поэтому ваша xml-конфигурация должна выглядеть так:
Надеюсь, что этот ответ будет понятным и поможет кому-то найти решение этой проблемы, любые вопросы не стесняйтесь спрашивать!
Начиная с 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'
Полезно, если у вас есть приложение для загрузки весны, и вы хотите сохранить возможность изменять функции jacksonа из файла application.properties
.
В случае Spring Data Rest тогда, когда решение, отправленное @ r1ckr, работает, все, что требуется, – это добавить одну из следующих зависимостей в зависимости от версии Hibernate:
com.fasterxml.jackson.datatype jackson-datatype-hibernate4 ${jackson.version}
или
com.fasterxml.jackson.datatype jackson-datatype-hibernate5 ${jackson.version}
В Spring Data Rest существует class:
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper
который автоматически определит и зарегистрирует модуль при запуске приложения.
Однако существует проблема:
Проблема Сериализация Lazy @ManyToOne
Если вы используете XML-конфигурацию и используете
, я обнаружил, что вам нужно вложить
внутри
как рекомендовано в учетной записи Jackson Github .
Вот так:
`
Для тех, кто пришел сюда, чтобы найти решение для службы RESTful на базе Apache CXF, конфигурация, которая его исправляет, приведена ниже:
Где HibernateAwareObjectMapper определяется как:
public class HibernateAwareObjectMapper extends ObjectMapper { public HibernateAwareObjectMapper() { registerModule(new Hibernate5Module()); } }
Следующая зависимость требуется по состоянию на июнь 2016 года (при условии, что вы используете Hibernate5):
com.fasterxml.jackson.datatype jackson-datatype-hibernate5 2.7.4
Вы можете использовать следующий помощник из проекта Spring Data Rest:
Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
Я попробовал полезный ответ @ rick , но столкнулся с проблемой, что «хорошо известные модули», такие как jackson-datatype-jsr310, не были автоматически зарегистрированы, несмотря на то, что они находятся на пути к classам. (Это сообщение в блоге объясняет автоматическую регистрацию.)
Развернувшись на ответе @ rick, вот вам вариант Jackson2ObjectMapperBuilder Spring для создания 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> converters) { converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } }
Хотя этот вопрос немного отличается от этого: исключение Strange Jackson при сериализации объекта 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 { @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);