본문 바로가기
Spring/JPA

[SpringBoot] JPA Collection 페이징 처리

by 진꿈청 2024. 1. 29.

JPA Collection 페치 조인 최적화 방법은 데이터가 뻥튀기 되기에 페이징 처리에 어려움이 있었다.

 

하지만, 다른 방법을 사용하면 Collection 페이징 처리가 가능한데 다음과 같은 방법을 따르면 된다.

 

 

방법

 

1. @XToOne(OneToOne, ManyToOne) 관계에 대해선 기존의 페치 조인을 활용한다. (컬렉션은 지연 로딩)

    @Override
    public List<Order> findAllWithMemberDelivery(Pageable pageable) {
        QOrder order = QOrder.order;
        QMember member = QMember.member;
        QDelivery delivery = QDelivery.delivery;

        return jpaQueryFactory
                .select(order)
                .from(order)
                .join(order.member, member)
                .fetchJoin()
                .join(order.delivery, delivery)
                .fetchJoin()
                .offset(pageable.getPageNumber())
                .limit(pageable.getPageSize())
                .fetch();
    }

 

2. 지연 로딩 최적화를 위해 application.yml에 hibernate.default_batch_fetch_size 크기를 지정한다.

  jpa: 
    hibernate:
      ddl-auto: create 
    properties: 
      hibernate: 
      # show_sql: true 
        format_sql: true 
        default_batch_fetch_size: 100

 

3. @BatchSize 어노테이션을 활용한 개별 최적화도 가능

@BatchSize(size = 100)

 

 

위의 옵션들을 사용하면 IN 쿼리를 사용하여 한꺼번에 필요한 엔티티들만 받아오게 된다.

BatchSize를 지정해 놓지 않으면 Collection에 페치 조인을 사용하지 않아 엄청나게 많은 쿼리가 나가게 된다.

하지만, BatchSize를 지정해 놓게 되면 크기 만큼 데이터를 한번에 가져온다.

 

또한, BatchSize를 사용하면 쿼리 수는 증가하지만,

앞 글에서 살펴본 데이터 뻥튀기가 일어나지 않는다.

즉, 쿼리가 필요한 정보만을 갖고 오게 된다.

 

따라서, 원래 1 + N개의 쿼리가 로직에 따라 (1 + 1, 1 + 1 + 1)만 나가게 된다.

 

Collection 페치 조인 방법과 적절한 트레이드 오프가 존재하지만, 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){

        Pageable pageable = PageRequest.of(offset, limit);

        List<Order> orders = orderRepository.findAllWithMemberDelivery(pageable);

        List<OrderDto> result = orders.stream()
                .map(OrderDto::new)
                .collect(Collectors.toList());

        return result;
    }

 

 

주의할 점

DB마다 다르긴 하지만 IN의 한계가 1000개인 DB가 있기 때문에,

BatchSize는 100 ~ 1000개 사이에서 적절하게 선택하는 것이 좋다.

 

그리고 BatchSize 크기를 10으로 하여도 불러오는 데이터의 양은 동일하여,

결국 사용되는 메모리는 똑같기 때문에 잘 판단해야 한다.

 

본 내용은 김영한 강사님의 인프런 JPA 강의를 토대로 작성되었습니다.