[스프링과 JPA활용 2편] 요약 정리5

sonnng·2023년 11월 3일
0

Spring

목록 보기
27/41

주문 조회 V3.1: 엔티티를 DTO로 변환 - 페이징과 한계돌파

  • 컬렉션을 페치조인하면 페이징이 불가능하다.

컬렉션을 페치조인시 일대다 조인이 발생하여 데이터 뻥튀기가 발생하며 예측할 수 없이 쿼리가 증가하게 되는걸 경험할 수 있다.

일대다에서 일(1) 기준으로 페이징을 하는 것을 목적으로 하지만, 데이터는 다(N)기준으로 row가 생성된다는 불일치성이 발생한다. Order 기준으로 페이징하고 싶은데 OrderItem으로 조인하면 OrderItem이 기준이 되어버리기 때문이다.

🫸이 경우, 모든 DB를 읽어서 메모리에 올려두고 메모리에서 페이징을 시도하므로 최악은 장애까지 이어질 수 있다는 우려가 있다.



그렇다면 페이징도 하고 컬렉션 엔티티도 조회하고 싶으면 어떻게 해야하나?

  1. 먼저 ToOne 관계인 엔티티들은 모두 페치조인으로 리포지토리 내용을 작성한다.

ToOne 관계는 row 수를 증가시키지 않으므로 페이징 쿼리에 영향을 미치지 않기 때문이다.

  1. 컬렉션은 지연로딩으로 조회한다.(페치조인 X)

  2. 지연로딩 성능 최적화를 위해서 hibernate.default_batch_fetch_size(전역설정) 또는 @BatchSize(개별 설정)을 적용한다.

이 옵션을 통해 조회한 엔티티에 관련된 컬렉션이나 프록시 객체들을 한꺼번에 설정한 size만큼 IN쿼리로 조회할 수 있다.

  • Order-OrderItem은 일대다 관계이다.

이 경우 컬렉션 조회로, 지연로딩을 해야하는데 먼저 Order이 조회될때 OrderItem은 batch size로 설정된 값만큼 모두 로딩되어진다.

  • OrderItem-Item은 다대일 관계이다.

이 경우 컬렉션 조회는 아니지만, 지연로딩을 해야하는데 먼저 Order이 조회될 때 프록시 객체로 설정되고 batch size로 설정된 값만큼 모두 로딩되어 진다.


Repository

public List<Order> findAllWithMemberDelivery(int offset, int limit) {
return em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class) 
.setFirstResult(offset)
.setMaxResults(limit) 
.getResultList();
}

api controller

/**
 * V3.1 엔티티를 조회해서 DTO로 변환 페이징 고려 
 * - ToOne 관계만 우선 모두 페치 조인으로 최적화
 * - 컬렉션 관계는 hibernate.default_batch_fetch_size, @BatchSize로 최적화 
 */
@GetMapping("/api/v3.1/orders")
public List<OrderDto> ordersV3_page(@RequestParam(value = "offset", 
defaultValue = "0") int offset,
@RequestParam(value = "limit", defaultValue
= "100") int limit) {
    List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, 
limit);
    List<OrderDto> result = orders.stream()
.map(o -> new OrderDto(o)) 
.collect(toList());
return result; 
}

최적화 옵션 설정(전역설정 방법에 해당)

spring:
jpa:
properties:
hibernate:
default_batch_fetch_size: 1000

😀 이렇게 했을 때 장점

  • 페치 조인과 비교해서 쿼리 호출 수가 조금 증가하긴 하지만 DB 데이터 전송량이 감소한다.
  • 컬렉션 페치 조인은 페이징이 불가능하지만, 이 방법은 페이징이 가능하다.
  • 조인보다 DB 데이터 전송량이 최적화된다.

결론

ToOne 관계는 페치조인을 해도 페이징에 영향을 주지 않는다. 따라서 이 관계는 페치조인으로 쿼리수를 줄이고 나머지는 전역설정이나 개별설정을 통해 배치 사이즈를 설정해 컬렉션, 프록시 객체들 조회를 최적화하도록 하자.

0개의 댓글