본문 바로가기
개발/JPA

[JPA] 지연로딩과 조회 성능 최적화 1 - xToOne

by Allonsy 2022. 4. 18.
반응형

# 아래 상황에서 최적화 방법을 알아보자

* ManyToOne, OneToOne

* 지연로딩

=> xToOne 연관관계의 대상을 프록시 객체로 가져옴

 

★Entity를 그대로 반환하는 것은 좋지 않음!

 

# 컨트롤러에서 xToOne 관계가 있는 entity 반환하는 방법

1) 컨트롤러에서 entity를 그대로 반환 시 아래와 같은 오류 발생!

 

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 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) (through reference chain: java.util.ArrayList[0]->jpabook.jpashop.domain.Order["member"]->jpabook.jpashop.domain.Member$HibernateProxy$2RozdIe4["hibernateLazyInitializer"])

 

원인 : jackson 라이브러리는 프록시 객체를 json으로 만들어주지 못한다

(지연로딩시 연관관계에 있는 객체는 프록시객체이다, ByteBuddy~~가 스프링에서 사용하는 프록시임!)

 

해결 방법 : Hibernate5Module을 스프링 빈으로 등록 (추천하지 않음, 코드도 첨부하지 않겠음. 실무에선 무조건 DTO를 만들자)

더 좋은 해결 방법 : entity를 반환하지 말고 DTO를 만들어서 반환하자!

 

2) 연관된 엔티티에 접근해서 엔티티를 강제 초기화 시키고 반환하기

    @GetMapping("/api/v1/simple-orders")
    public List<Order> ordersV1() {
        List<Order> all = orderRepository.findAllByString(new OrderSearch());
        for (Order order : all) {
            //getMember() 까지는 프록시 객체, getName()을 할때 가져옴
            order.getMember().getName(); // 강제 초기화
            order.getDelivery().getAddress(); // 강제 초기화
        }
        return all;
    }

이렇게 하면 연관된 엔티티를 가져와서 반환할 수 있다

더 좋은 해결 방법 : entity를 반환하지 말고 DTO를 만들어서 반환하자!

 

3) fetch 타입을 EAGER로 설정해서 실제 엔티티를 즉시 가져오게 설정해서 반환하기

강제 초기화하는 코드가 귀찮다고 이렇게 설정하면 더더욱 안된다

강제로 엔티티를 초기화하지 않기 위해 fetch 타입을 LAZY에서 EAGER로 설정하면 안 됨!

=> 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 항상 조회해오기 때문에 성능이 안좋다!!

더 좋은 해결 방법 : entity를 반환하지 말고 DTO를 만들어서 반환하자!

 

# 결론 (너무 중요해서 전부 다 핑크색)

항상 지연로딩을 기본으로! (성능최적화가 필요하면 fetch join사용)

DTO를 이용해서 반환하기!

**엔티티를 직접 반환할 때 양방향 연관관계가 걸린 곳은 한 곳에 @JsonIgnore 어노테이션을 달아주어야 무한루프에 빠지지 않는다!

 

인프런 - 실전! 스프링 부트와 JPA 활용2 - API개발과 성능최적화(김영한 강사님) 강의를 수강하며 정리한 글입니다 :)

반응형

댓글