에러 일기

[SPRING] No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)

재원쓰 2023. 1. 10. 16:41

엔티티 객체와 다른 엔티티 객체가 연관관계에 놓여있을 때, 이것을 조회하면 무한루프에 걸릴 위험이 있기 때문에 한쪽에서 @JsonIgnore를 선언해주어서, 제대로 값이 응답되길 바라며 포스트맨을 시도했다.

그러나 이런 에러가 나왔다.

연관관계 양쪽을 전부 @JsonIgnore를 했을 때는 에러가 나오지 않고, 응답받은 값에 연관관계 데이터가 아예 사라진 모습이다.

 

개념적으로 접근한다면,

연관관계 객체가 현재 자기 자신이 아니라, 프록시객체로 되어있는 상태이다. 에러 내용에 있는 'bytebuddy' 이게 프록시 객체를 만들어주는 라이브러리이다.

조회할 데이터에서는 연관관계 객체를 조회하기 위해 해당 객체명을 찾았을텐데, 그 객체가 프록시로 바뀌면서 형태가 변형된 것이다. 그래서 에러가 떳던 것이다.

 

Hibernate5Module

이것을 해결하기 위해서 Hibernate5Module를 사용해볼 수 있다. Hibernate5Module은 LAZY로 되어있는 연관관계 객체를 다 무시해주는 역할을 한다. 그래서 실제 불러온 데이터에선 연관관계 데이터쪽이 null로 찍혀 있는 것을 볼 수 있다.

 

사용법

https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-hibernate5

여기서 Gradle을 가져온다.

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
// 가져올 때 version은 빼고 name을 합쳤다.
application쪽에 Hibernate5Module를 추가해준다.
@SpringBootApplication
public class JpashopApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpashopApplication.class, args);
    }

    @Bean
    Hibernate5Module hibernate5Module(){
        return new Hibernate5Module();
    }
}

이러고 테스트해보니 아래와 같이 나온다.

 

여기서 LAZY로 되어있는 데이터를 조회해주고싶다면 Hibernate5Module에 설정을 하나 추가해주면 된다.

@Bean
Hibernate5Module hibernate5Module(){
    Hibernate5Module hibernate5Module = new Hibernate5Module();
    hibernate5Module.configure(Hibernate5Module.Feature.FORCE_LAZY_LOADING, true); // 👈 추가 설정
    return hibernate5Module;
}

이러고 다시 테스트해보면 아래와 같이 LAZY 객체의 데이터도 전부 불러와진다.

 

하지만 이건 또 모든 내용을 다 조회해온다는 단점이 있다. 내가 원하는 데이터만 조회해오고 나머지는 null로 뜬다면 더 좋을텐데...

아래와 같이 내가 조회하고 싶은 객체를 강제로 LAZY로딩을 해버린다
객체에 getter로 멤버변수 하나만 가져와도 LAZY로딩이 실행되기 때문에 원하는 것을 가져올수있다.

@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1(){
    List<Order> all = orderRepository.findAllByString(new OrderSearch());
    for(Order order:all){ // 👈 아래와 같이 getter 한번만 실행시켜줘도 LAZY 로딩이 된다.
        order.getMember().getName();
        order.getDelivery().getAddress();
    }
    return all;
}