[java] 가져 오지 않은 지연 개체에 대한 Jackson 직렬화 방지

User 개체를 반환하는 간단한 컨트롤러가 있으며이 사용자는 최대 절전 속성 FetchType.LAZY가있는 특성 좌표를 가지고 있습니다.

이 사용자를 얻으려고 할 때 항상 사용자 개체를 가져 오기 위해 모든 좌표를로드해야합니다. 그렇지 않으면 Jackson이 User를 직렬화하려고 할 때 예외가 발생합니다.

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에게 가져 오지 않은 객체를 직렬화하지 않도록 지시하는 방법이 있습니까? jackson-hibernate-module을 구현하기 전에 3 년 전에 게시 된 다른 답변을 찾고 있습니다. 그러나 아마도 새로운 jackson 기능으로 달성 할 수 있습니다.

내 버전은 다음과 같습니다.

  • 봄 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 도 생성 할 필요가 없습니다. Spring이 이미 가지고 있는 기본 MappingJackson2HttpMessageConverter에 Hibernate4Module 을 추가하고이를 애플리케이션의 HttpMessageConverters에 추가하기 만하면 됩니다.

  1. WebMvcConfigurerAdapter 에서 스프링 구성 클래스를 확장하고 configureMessageConverters 메서드를 재정의합니다 .

  2. 그 방법에 추가 에서 이전 메서드에 등록 된 Hibernate4Module 과 함께 MappingJackson2HttpMessageConverter 를 .

구성 클래스는 다음과 같아야합니다.

@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


답변

이것은 @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'

다음이있는 경우 유용합니다. 응용 프로그램과 application.properties파일 에서 Jackson 기능을 수정하는 기능을 유지하려고 합니다.


답변

Spring Data Rest의 경우 @ 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>

SpringData Rest에는 클래스가 있습니다.

org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper

애플리케이션 시작시 모듈을 자동 감지하고 등록합니다.

그러나 문제가 있습니다.

Lazy @ManyToOne 직렬화 문제


답변

XML 구성을 사용 하고을 사용 하는 경우 Jackson Github 계정 에서 권장하는대로 내부 <annotation-driven />에 중첩해야한다는 것을 알았습니다.<message-converters><annotation-driven> .

이렇게 :

<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 서비스에 대한 솔루션을 찾기 위해 여기에 온 사람들을 위해이를 수정하는 구성은 다음과 같습니다.

<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 4.3, (non-boot) & Hibernate 5.1에 대한 것입니다. 여기서 우리 는 성능상의 이유로 모든 fetchtype을 fetch=FetchType.LAZY케이스에서 전환했습니다 fetch=FetchType.EAGER. 즉시 우리는com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy지연로드 문제로 인한 예외를 했습니다.

먼저 다음 Maven 종속성을 추가합니다.

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>

그런 다음 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;
}

메모:

  • 이 모듈없이 Jackson과 유사한 동작을 얻으려면 Hibernate5Module을 만들고 구성해야합니다. 기본값은 호환되지 않는 가정을합니다.

  • 우리 WebMvcConfigurerAdapter는 다른 많은 구성을 가지고 있고 다른 구성 클래스를 피하고 싶었습니다. 이것이 WebMvcConfigurationSupport#addDefaultHttpMessageConverters다른 포스트에서 언급 된 함수를 사용하지 않은 이유 입니다.

  • WebMvcConfigurerAdapter#configureMessageConverters메시지 변환기의 모든 Spring 내부 구성을 비활성화합니다. 우리는 이와 관련된 잠재적 인 문제를 피하는 것을 선호했습니다.

  • 사용하여 extendMessageConverters다른 모든 메시지 컨버터의 구성을 잃지 않고 모든 자동 구성 잭슨 클래스에 지원 액세스를.

  • 사용 하여 기존 변환기에 getObjectMapper#registerModule를 추가 할 수있었습니다 Hibernate5Module.

  • 모듈이 JSON 및 XML 프로세서 모두에 추가되었습니다.

이 추가로 Hibernate 및 지연 로딩 문제가 해결되었지만 생성 된 JSON 형식에 잔여 문제가 발생했습니다 . 이 github 문제 에서보고 된 것처럼 hibernate-jackson 지연로드 모듈은 현재 @JsonUnwrapped 주석을 무시하여 잠재적 인 데이터 오류를 유발합니다. 이것은 강제 로딩 기능 설정에 관계없이 발생합니다. 문제는 2016 년부터있었습니다.


노트

지연로드 된 클래스에 다음을 추가하면 내장 ObjectMapper가 hibernate5 모듈을 추가하지 않고도 작동하는 것으로 보입니다.

@JsonIgnoreProperties(  {"handler","hibernateLazyInitializer"} )
public class Anyclass {