가져 오지 않은 지연 개체에 대한 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.