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


84

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, 튜토리얼을 보았습니다. 봄 3.2.5에는 이미 MappingJackson2HttpMessageConverter가 있지만 가져 오지 않은 게으른 객체를 피하는 데 사용할 수는 없으며 튜토리얼에서 구현하려고 시도했지만 아무것도 없습니다. ..
— r1ckr 2014

1
동일한 오류 "프록시를 초기화 할 수 없습니다"또는 다른 오류가 발생합니까? (나는 Spring 3.2.6, Hibernate 4.1.7 및 jackson 2.3 기사에 설명 된 추가 HibernateAwareObjectMapper를 사용했습니다.)
— indybee

1
알았다!! 버전 3.1.2부터 Spring에는 자체 MappingJackson2HttpMessageConverter 가 있으며 자습서의 동작과 거의 동일한 동작이 있지만 문제는 Spring Java 구성을 사용하고 있으며 javaconfigs로 시작한다는 것입니다. 나는 그것을 Spring의 HttpMessageConverters에 추가하는 대신 @Bean MappingJackson2HttpMessageConverter 를 만들려
— 고했다.

이 :) 노력하고 기뻐
— indybee

답변:


94

마침내 해결책을 찾았습니다! 나에게 단서를 준 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>

이 답변이 이해하기 쉽고 누군가가이 문제에 대한 해결책을 찾는 데 도움이되기를 바랍니다. 질문이 있으면 언제든지 물어보십시오!


3
솔루션을 공유해 주셔서 감사합니다. 그러나 몇 번의 시도 끝에 현재 최신 잭슨 ( 2.4.2 )이 봄 4.0.6에서 작동 하지 않음을 발견했습니다 . 여전히 "세션 없음"오류가 있습니다. jackson 버전을 2.3.4 (또는 2.4.2)로 다시 내려 갔을 때만 작동하기 시작했습니다.
— Robin

1
MappingJackson2HttpMessageConverter를 지적 해 주셔서 감사합니다! 이런 수업을 잠시 찾고있었습니다.
— LanceP

1
대단히 감사합니다! 방금 테스트했고 Spring 4.2.4, Jackson 2.7.1-1 및 JacksonDatatype 2.7.1과 함께 작동합니다. :)
— Isthar

1
이것은 SpringData REST의 문제를 해결하지 않습니다.
— Alessandro C

1
Spring 대신 Springboot를 사용하는 경우 동일한 문제를 해결하는 방법은 무엇입니까? Hibernate4 모듈 (현재 내가 가지고있는 버전) 등록을 시도했지만 문제가 해결되지 않았습니다.
— ranjith Gampa

34

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


19
Hibernate 5의 경우 (현재 Spring Boot 버전에서와 같이) 다음과 같습니다. Hibernate5Module . 추가로 종속성이 필요합니다<groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-hibernate5</artifactId>
— Zeemee

1
Spring Boot 2.1에서도 나를 위해 일했습니다. 나를 미치게 만들기 시작했다.
— 반담

이것을 구현하는 방법은 명확하지 않습니다. 호환되지 않는 유형 오류가 발생합니다.
— EarthMind

2
대박! Spring Boot 2.0.6 및 Hibernate 5로 나를 위해 일했습니다.
— GarouDan

이것은 매우 도움이되었습니다. Hibernate5Module 클래스 인스턴스를 빈으로 등록하는 것만으로는 충분하지 않다는 점을 지적해야합니다. 단일 ObjectMapper 인스턴스에 적용하려면 모듈을 Spring 관리 클래스에 자동 연결 한 다음 ObjectMapper 인스턴스에 등록 할 수 있습니다. @Autowire 모듈 jacksonHibernateModule; ObjectMapper 매퍼 = new ObjectMapper (); mapper.registerModule (this.jacksonHibernateModule).
— gburgalum01 19

13

이것은 @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 기능을 수정하는 기능을 유지하려고 합니다.


application.properties파일을 사용하여 어떻게 이것을 달성 할 수 있습니까 ( 게으른로드 된 객체를 피하기 위해 Jackson 구성) ?
— hemu

1
그것은 여기서 말하는 것이 아닙니다. @Luis Belloch의 솔루션은 Spring Boot 기본 메시지 변환기를 재정의하지 않고 jackson 속성을 제어하기 위해 application.properties 값을 계속 사용하도록 Jackson2ObjectMapperBuilder를 만들 것을 제안합니다.
— kapad

죄송합니다 ...이 방법을 어디에서 구현 하시겠습니까? 봄 부팅으로는 WebMvcConfigurerAdapter가되지 않습니다 스프링 (5) 사용
— 에릭 황

Hibernate4Module레거시는
— 드미트리 Kaltovich

8

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 직렬화 문제


SpringData Rest를 사용하지 않을 때 Spring Boot로 어떻게 구성합니까?
— Eric Huang

충분하지 않은. 당신은 또한 필요@Bean hibernate5Module()
— 드미트리 Kaltovich을

4

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>

`


XML의 설정은 유산이다
— 드미트리 Kaltovich

3

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>  

1

다음 솔루션은 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 {

이 솔루션은 훨씬 더 쉬울 수있다 : kotlin @Bean fun hibernate5Module(): Module = Hibernate5Module()
— 드미트리 Kaltovich


0

이 문제에 대한 아주 간단한 해결책을 만들었습니다.

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public Set<Pendency> getPendencies() {
    return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>();
}

제 경우에는 오류를 표시했습니다. 왜냐하면 나는 펜던시를 반환 할 때마다 수정할 수없는 목록으로 변환했지만 인스턴스를 가져 오는 데 사용한 방법에 따라 게 으르거나 아닐 수도 있기 때문입니다. (페치 유무에 관계없이) Hibernate에 의해 초기화되기 전에 테스트를 수행하고 빈 속성을 직렬화하는 것을 방지하고 내 문제를 해결하는 주석을 추가합니다.


0

나는 하루 종일 같은 문제를 해결하기 위해 노력했습니다. 기존 메시지 변환기 구성을 변경하지 않고 수행 할 수 있습니다.

제 생각에는 jackson-datatype-hibernate의 도움으로 2 단계 만으로이 문제를 해결하는 가장 쉬운 방법입니다 .

kotlin 예제 (Java와 동일) :

  1. 추가 build.gradle.kts :
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
  1. 창조하다 @Bean
   @Bean
   fun hibernate5Module(): Module = Hibernate5Module()

  • 그것을주의해라 Module 입니다 com.fasterxml.jackson.databind.Module,하지java.util.Module

  • 또한 @JsonBackReference& @JsonManagedReference에 @OneToMany& @ManyToOne관계 를 추가하는 것도 좋은 방법입니다 . @JsonBackReference수업에서 단 1 일 수 있습니다.



-1

나는 이것을 시도하고 일했습니다.

// 지연로드를위한 맞춤 구성

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);


-1

@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(왜냐하면 im using hibernate 5), jackson은 초기화되지 않은 객체의 프록시를 무시하지만, 내가 당신의 접근 방식을 사용하더라도 "잘 알려진 모듈"이 등록되지 않은 것 같습니다. 내 응용 프로그램의 일부가 손상되어 모듈을 제거하면 다시 작동하기 때문에 이것을 알고 있습니다.
— Daniel Henao

@DanielHenao spring.io/blog/2014/12/02/… 공부 했 습니까? Jackson-Antpath-Filter가 제안한대로 DelegatingWebMvcConfiguration클래스 (대신 WebMvcConfigurerAdapter)를 사용하여 Hibernate5로 전환했습니다 .@Override public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) { messageConverters.add(jacksonMessageConverter()); addDefaultHttpMessageConverters(messageConverters); }
— Markus Pscheidt

1. Hibernate4Module유산입니다. 2 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS-하지 질문에 대한
— 드미트리 Kaltovich

@DmitryKaltovich ad 1) 작성 당시에는 유산이 아니 었습니다. ad 2) 좋아요, 제거되었습니다.
— Markus Pscheidt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.