programing

가져오기되지 않은 지연 개체에서 잭슨 직렬화 방지

newsource 2022. 11. 16. 21:17

가져오기되지 않은 지연 개체에서 잭슨 직렬화 방지

사용자 개체를 반환하는 단순한 컨트롤러가 있으며, 이 사용자는 최대 절전 모드 속성 FetchType을 가진 속성 좌표를 가지고 있습니다.게으른.

이 사용자를 취득하려고 하면 항상 모든 좌표를 로드하여 사용자 객체를 취득해야 합니다.그렇지 않으면 잭슨이 사용자를 시리얼화하려고 하면 예외가 발생합니다.

com.sysml.syslog.syslogind.syslogindJsonMappingException: 프록시를 초기화할 수 없습니다. 세션 없음

이는 잭슨이 이 에칭되지 않은 개체를 가져오려고 하기 때문입니다.오브젝트는 다음과 같습니다.

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년 전에 올라온 다른 답변들을 찾아봤어요하지만 아마도 새로운 잭슨 기능으로 달성할 수 있을 것이다.

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

  • 스프링 3.2.5
  • 휴지 상태 4.1.7
  • 잭슨 2.2

잘 부탁드립니다.

나는 마침내 해결책을 찾았다! 힌트를 준 인디비 덕분에.

Spring 3.1, Hibernate 4 및 Jackson-Module-Hibernate 튜토리얼은 Spring 3.1 이전 버전에 적합한 솔루션입니다.단, 버전 3.1.2 Spring에는 독자적인 MappingJackson2가 있습니다.튜토리얼과 거의 같은 기능을 가진 HttpMessageConverter이므로 이 커스텀HTTPMessageConverter를 작성할 필요가 없습니다.

javaconfig에서는 hibernate Aware Object Mapper를 만들 필요가 없습니다.기본 Mapping Jackson2hibernate4 Module을 추가하면 됩니다.Spring이 이미 가지고 있는HttpMessageConverter를 어플리케이션의 HttpMessageConverters에 추가합니다.따라서 다음 작업을 수행해야 합니다.

  1. WebMvcConfigrAdapter에서 spring config 클래스를 확장하고 configureMessageConverters 메서드를 덮어씁니다.

  2. 이 방법으로 MappingJackson2를 추가합니다.Http Message ConverterHibernate4 Module이 Previus 메서드로 등록되어 있습니다.

설정 클래스는 다음과 같습니다.

@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 설정이 있는 경우 MappingJackson2를 직접 작성할 필요가 없습니다.Http Message Converter도 마찬가지지만 튜토리얼(Hibernate Aware Object Mapper)에 표시되는 퍼스낼라이즈된 매퍼를 작성해야 합니다.그러면 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를 사용하여 Hibernate4 모듈을 등록하는 것은 설정에 다음과 같이 간단하게 할 수 있습니다.

@Bean
public Module datatypeHibernateModule() {
  return new Hibernate4Module();
}

참조: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

이는 @rick이 수용하는 솔루션과 유사합니다.

메시지 을 터치하지 " " "를 .Jackson2ObjectMapperBuilderlike : 류:: :

@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.propertiesfilename을 클릭합니다.

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>

Spring Data Rest 내에는 다음과 같은 클래스가 있습니다.

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

응용 프로그램 시작 시 모듈을 자동으로 감지하고 등록합니다.

단, 다음과 같은 문제가 있습니다.

Lazy @ManyToOne 시리얼화 문제

나는 같은 문제를 풀려고 하루 종일 애를 썼다.기존 메시지 변환기 설정을 변경하지 않고 이 작업을 수행할 수 있습니다.

이 문제를 해결하는 가장 쉬운 방법은 잭슨-데이터타입-하이버네이트의 도움을 받아 2단계만으로 해결할 수 있다고 생각합니다.

kotlin 예제(java와 동일):

  1. ★★build.gradle.kts:
implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
  1. @Bean
   @Bean
   fun hibernate5Module(): Module = Hibernate5Module()

  • 「 」라는 점에 해 주세요.Modulecom.fasterxml.jackson.databind.Module 아니라, 이에요.java.util.Module

  • 또, & 를 추가하는 것도 좋은 방법입니다.@JsonManagedReference로로 합니다.@OneToMany&@ManyToOne★★★★★★★★★★★★★★★★★★.@JsonBackReference1번으로 하다

Configuration을 사용하는 <annotation-driven />는 네가 를 틀어야 <message-converters>에 inside inside inside <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>

`

다음 .1 을 spring 4.3 (비부트)&hibernate 5.1로 했습니다.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;
}

주의:

  • 이 모듈을 사용하지 않고 잭슨과 유사한 동작을 얻으려면 Hibernate5 모듈을 만들고 구성해야 합니다.기본적으로는 호환되지 않는 가정입니다.

  • 우리들의WebMvcConfigurerAdapter다른 설정이 많이 포함되어 있기 때문에 다른 컨피규레이션클래스는 피하고 싶기 때문에WebMvcConfigurationSupport#addDefaultHttpMessageConverters다른 게시물에서 참조된 함수입니다.

  • WebMvcConfigurerAdapter#configureMessageConverters메시지 변환기의 스프링 내부 구성을 모두 비활성화합니다.우리는 이 문제를 회피하는 것을 선호했다.

  • 사용.extendMessageConverters그럼 다른 모든 메시지컨버터의 설정이 손실되지 않고 자동으로 설정된 모든 잭슨 클래스에 액세스할 수 있습니다.

  • 사용.getObjectMapper#registerModule우리는 이 모든 것을 추가할 수 있었다.Hibernate5Module기존 컨버터에 접속합니다.

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

이 추가에 의해, 휴지 상태나 로딩의 문제는 해결되었지만, 생성된 JSON 포맷에서는 아직 문제가 발생.github 문제에서 보고되었듯이 현재 휴지 상태 잭슨의 저속 로드모듈은 @JsonUnwraped 주석을 무시하므로 잠재적인 데이터 오류가 발생할 수 있습니다.이는 강제 로딩 기능 설정에 관계없이 발생합니다.문제는 2016년부터 있었다.


메모

loaded가 느린 클래스에 다음 항목을 추가하면 내장된 Object Mapper는 hibernate5 모듈을 추가하지 않고도 작동합니다.

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

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 Data Rest 프로젝트에서 다음 도우미를 사용할 수 있습니다.

Jackson2DatatypeHelper.configureObjectMapper(objectMapper);

나는 이 문제에 대해 매우 간단한 해결책을 만들었다.

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

이 경우 펜던스를 반환할 때마다 수정할 수 없는 목록으로 변환했지만 인스턴스(페치 유무)에 따라 지연 여부를 테스트하고 Hibernate에 의해 초기화되기 전에 주석을 추가합니다.빈 부동산을 연재해서 제 문제를 해결했어요.

우리는 전화해야 하는 문제를 해결할 수 없었다.Hibernate.initialize(objX.getAttributeY())모든 Attribute에 대해서요.여기서는 엔티티 주석이 있는 모든 속성에서 초기화 메서드를 호출하는 utils 클래스를 작성했습니다.수집은 여전히 별도로 처리해야 합니다.

import org.hibernate.Hibernate;

import javax.persistence.Entity;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class HibernateInitialiseUtil<T> {

    private final List<Method> entityGetterMethods;

    public HibernateInitialiseUtil(Class<T> clazz) {
        this.entityGetterMethods = this.getGettersMethodsOfEntityAttributes(clazz);
    }

    public void initializeAllEntityAttributes(T obj) throws InvocationTargetException, IllegalAccessException {
        for (Method getter : this.entityGetterMethods) {
            Hibernate.initialize(getter.invoke(obj));
        }
    }

    private List<String> getNameOfEntityAttributes(final Class<T> targetClass) {
        Field[] attributes =  targetClass.getDeclaredFields();
        List<String> fields = new ArrayList<>();
        for (Field attribute : attributes) {
            if (attribute.getType().isAnnotationPresent(Entity.class)) {
                fields.add(attribute.getName());
            }
        }
        return fields;
    }

    private List<Method> getGettersMethodsOfEntityAttributes(final Class<T> targetClass) {
        List<Method> resultList = new ArrayList<>();
        for (String attributeName : getNameOfEntityAttributes(targetClass)) {
            try {
                PropertyDescriptor pd = new PropertyDescriptor(attributeName, targetClass);
                Method getter = pd.getReadMethod();
                resultList.add(getter);
            } catch (IllegalArgumentException | IntrospectionException e) {
                e.printStackTrace();
            }
        }
        return Collections.unmodifiableList(resultList);
    }
}

질문은 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 = new JacksonMapper();
    SimpleModule simpleModule = new SimpleModule(
            "SimpleModule", new Version(1,0,0,null)
    );
    simpleModule.addSerializer(
            JavassistLazyInitializer.class,
            new HibernateLazyInitializerSerializer()
    );
    mapper.registerModule(simpleModule);

@rick의 편리한 답변을 시험해 보았습니다만, jackson-datatype-jsr310등의 「잘 알려진 모듈」이 클래스 패스에 있어도 자동적으로 등록되지 않는 문제에 부딪혔습니다.( 블로그의 투고에서는, 자동 등록에 대해 설명합니다.)

@rick의 답변에 대한 자세한 내용은 Spring의 Jackson2ObjectMapperBuilder를 사용하여 다음을 작성하는 변형입니다.ObjectMapper는 "을 자동으로 하고 "well-known module" 기능을 설정합니다.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);
    }
}

부트의 또 설정: " " " " " " " 。spring.jpa.open-in-view=true

언급URL : https://stackoverflow.com/questions/21708339/avoid-jackson-serialization-on-non-fetched-lazy-objects