가져 오지 않은 지연 객체에 대한 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에 추가하기 만하면 됩니다.
WebMvcConfigurerAdapter 에서 스프링 구성 클래스를 확장하고 configureMessageConverters 메서드를 재정의합니다 .
이 메서드 에서 이전 메서드에 등록 된 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
애플리케이션 시작시 모듈을 자동 감지하고 등록합니다.
그러나 문제가 있습니다.
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>
SpringData Rest 프로젝트에서 다음 도우미를 사용할 수 있습니다.
Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
다음 솔루션은 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
disables all of Spring's internal configuration of message converters. We preferred to avoid the potential issues around this.Using
extendMessageConverters
enabled access to all the automatically-configured Jackson classes without losing the configuration of all other message converters.Using
getObjectMapper#registerModule
we were able to add theHibernate5Module
to the existing converters.- The module was added to both the JSON and XML processors
This addition solved the issue with Hibernate and lazy loading but caused a residual issue with the generated JSON format. As reported in this github issue, the hibernate-jackson lazy load module currently ignores the @JsonUnwrapped annotation, leading to potential data errors. This happens regardless of the force-loading feature setting. The problem has been there since 2016.
Note
It appears that by adding the following to classes that are lazy-loaded, the built-in ObjectMapper works without adding the hibernate5 module:
@JsonIgnoreProperties( {"handler","hibernateLazyInitializer"} )
public class Anyclass {
I tried @rick's useful answer, but ran into the problem that "well-known modules" such as jackson-datatype-jsr310 weren't automatically registered despite them being on the classpath. (This blog post explains the auto-registration.)
Expanding on @rick's answer, here's a variation using Spring's Jackson2ObjectMapperBuilder to create the ObjectMapper
. This auto-registers the "well-known modules" and sets certain features in addition to installing the 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);
}
}
Although this question is slightly different to this one : Strange Jackson exception being thrown when serializing Hibernate object, the underlying problem can be fixed in the same way with this code:
@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);
이 문제에 대한 아주 간단한 해결책을 만들었습니다.
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public Set<Pendency> getPendencies() {
return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>();
}
제 경우에는 오류를 표시했습니다. 왜냐하면 나는 펜던시를 반환 할 때마다 수정할 수없는 목록으로 변환했지만 인스턴스를 가져 오는 데 사용한 방법에 따라 게 으르거나 아닐 수도 있기 때문입니다. (페치 유무에 관계없이) Hibernate에 의해 초기화되기 전에 테스트를 수행하고 빈 속성을 직렬화하는 것을 방지하고 내 문제를 해결하는 주석을 추가합니다.
참고 URL : https://stackoverflow.com/questions/21708339/avoid-jackson-serialization-on-non-fetched-lazy-objects
'developer tip' 카테고리의 다른 글
Windows .cmd의 rm 및 mv와 동일 (0) | 2020.10.29 |
---|---|
canvas.toDataURL () SecurityError (0) | 2020.10.29 |
Swift에서 매개 변수로 이미지 업로드 (0) | 2020.10.29 |
팬더 : 이름이 X로 시작하는 모든 열을 선택하는 가장 좋은 방법 (0) | 2020.10.29 |
KDiff3 :“선 끝 스타일 충돌이 있습니다.” (0) | 2020.10.29 |