JPA :: N+1 문제 해결방안 및 trade-off (일대일, 다대일)

숑숑·2022년 2월 15일
0

JPA

목록 보기
2/4

N+1 문제란?

  • 쿼리 요청을 단 한 번 했음에도 불구, 연관관계 엔티티로 인해 데이터 개수(N) 만큼 추가 쿼리 요청이 발생하는 문제

방법 1. fetch join 후 DTO 변환

public List<Order> findAllWithMemberDelivery() {
 return em.createQuery(
 "select o from Order o" +
 " join fetch o.member m" +
 " join fetch o.delivery d", Order.class)
 .getResultList();
}
  • 연관관계에 있는 필드 모두 쿼리 1번으로 조회한다.
  • fetch join으로 다른 객체 모두 로딩된 상태 -> 지연 로딩 발생 X
  • 후에 결과 리스트를 DTO 리스트로 변환하는 루프를 따로 태워줘야 한다.

방법 2. DTO로 바로 조회

public List<OrderSimpleQueryDto> findOrderDtos() {
 return em.createQuery(
 "select new
jpabook.jpashop.repository.order.simplequery.OrderSimpleQueryDto(o.id, m.name,
o.orderDate, o.status, d.address)" +
 " from Order o" +
 " join o.member m" +
 " join o.delivery d", OrderSimpleQueryDto.class)
 .getResultList();
 }
  • 원하는 데이터를 직접 선택한다. 조금 더 native SQL에 가까운 방식이다.
  • 애플리케이션 네트워크 용량을 최적화할 수 있다.
  • 별도 변환 과정이 필요하지 않으므로, 방법 1 보다 응답 속도가 빠르다.
  • 그러나 특별한 환경이 아니라면, 최적화 정도가 드라마틱하진 않다고 한다.
  • 또한, 웹 로직이 repository 계층에 개입하기 때문에 계층 관계가 깨진다.
    • 즉, 타 API에서 재사용은 불가능에 가깝다.

Trade-Off

방법 1

  • 장점: 유지보수성, 재사용성
  • 단점: 방법 2보다 느린 속도

방법 2

  • 장점: 최적화된 성능
  • 단점: 재사용성, 저수준 정책에 대한 의존성, 코드 가독성

결론

뭐가 더 좋다고 단언할 수는 없다.

아래와 같은 순서로 최적화 방식을 선택하자.

  1. 무조건 방법 1 우선 선택 -> 대부분의 성능 이슈가 해결된다.
  2. 그래도 안 될 시에는 방법 2 를 고려한다.
  3. 최후의 방법으로는, JPA가 제공하는 native SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.
profile
툴 만들기 좋아하는 삽질 전문(...) 주니어 백엔드 개발자입니다.

0개의 댓글