Избегайте сериализации Джексона на не извлеченных ленивых объектах



у меня есть простой контроллер, который возвращает объект пользователя, у этого пользователя есть координаты атрибута, которые имеют свойство 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


спасибо заранее.

802   10  

10 ответов:

Я, наконец, нашел решение! спасибо индайби за подсказку.

учебник Spring 3.1, Hibernate 4 и Jackson-Module-Hibernate есть хорошее решение для весны 3.1 и более ранних версий. Но начиная с версии 3.1.2 весна имеет свой собственный MappingJackson2HttpMessageConverter С почти такой же функциональностью, как и в учебнике, поэтому нам не нужно создавать этот пользовательский HTTPMessageConverter.

С javaconfig мы этого не делаем нужно создать HibernateAwareObjectMapper тоже, нам просто нужно добавить Hibernate4Module по умолчанию MappingJackson2HttpMessageConverter что весна уже есть и добавить его в HttpMessageConverters приложения, так что нам нужно:

  1. расширьте наш класс конфигурации spring от WebMvcConfigurerAdapter и переопределить метод configureMessageConverters.

  2. на этот метод добавить 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

который будет автоматически обнаруживать и регистрировать модуль при запуске приложения.

однако есть проблема:

Проблема Сериализации Lazy @ManyToOne

если вы используете 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

    Ничего не найдено.