JPA

[JPA] Fetch join과 pagination

kidmillionaire1998 2024. 8. 12. 12:15

문제상황

현재 프로젝트에서는 모집글(Recruit)와 모집글에 들어갈 이미지(RecruitImage)가 1대다의 관계로 존재하며 양방향 매핑이 되어있는 상황이다. N + 1문제를 방지하고자 둘을 fetch join하여 가져오려고 하였다. 

 
위의 쿼리를 실행하였더니 다음과 같은 경고 메시지가 발생하였다.  

HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!

 

사실 페이징하려는 대상이 join 이후에 컬럼을 페이징하는 것이 아니라 모집글을 대상으로 하여 페이징을 처리를 하고 다의 관계에 있는 모집글 이미지를 양방향 매핑 조회 객체를 통해 끌어오기 위함이기 때문에, 결과 자체는 원하는 대로 반환이 되었다. 

 

하지만, 경고 메시지를 보게 되면 조건에 해당하는 모든 데이터를 가져와 메모리에 올려두고 사용한다라고 되어있다. 

조건에 해당 하는 데이터 전체를 가져오기 때문에 당연히 성능 상 이슈가 되며, 이를 메모리에 올려두고 페이징을 처리하니 이중으로 성능에 큰 영향을 준다.

 

조회 방향을 반대로? 

만약 join을 한 후의 데이터를 기준으로 페이징을 하려면 조회를 반대 방향으로 바꿔 자식 클래스에서 조회하여 부모 클래스를 페치 조인하여 해결할 수 있다. (예: 특정 게시판의 댓글을 페이징 등) 만약 위에서 처럼 부모클래스에서 조회하면 경고메시지와 별개로 부모 엔티티 기준 페이징이 수행되어 원하는 결과를 얻을 수 없다. 

하지만, 현재 내 상황은 join한 데이터 기준이 아니라 부모 클래스 기준으로 페이징이 되어야하므로 조회 방향을 바꾸는 것으로는 해결이 안되고 부모클래스에서 조회를 하되 다른 해결책을 찾아야한다. 

Batch Size 적용 

이를 해결하기 위한 방법 중에 하나가 반대 방향으로 fetch join을 적용하는 방법이다. @BatchSize을 지정하는 방법도 있지만 전역적으로 설정하기 위해 default_batch_fetch_size 옵션을 application.yml에 지정한다. 

  jpa:
    properties:
      hibernate:
        default_batch_fetch_size: 100

 

여기서 batch size의 의미는 무엇일까? 우선 이를 적용했을 때 발생하는 쿼리를 보자 

1. join이 일어나지 않고 우선 부모 테이블인 recruit의 값들을 조회해온다.  

2. 자식 테이블인 recruit_image에서 조회가 되는데, in절안에 앞의 쿼리에서 조회된 recruit의 id값이 들어간다. 

 

 

즉, 여기서 Batch Size 옵션에 할당되는 숫자는 IN 구문에 넣을 부모 엔티티 Key(ID)의 최대 개수를 의미한다. 우선 부모 엔티티를 페이징한 후 그에 해당하는 자식 엔티티들을 IN 구문을 통해 조회하는 것이다.  

참고로 부모테이블 페이지의 size가 1일 경우에는 IN절 대신에 동등 비교를 통해 조회해오는 것을 볼 수 있다. 

메모리에서의 페이징을 사전 차단하려면?

HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!  에러 메시지는 경고 메시지이기 때문에 실행 자체는 되며, 이는 개발자가 쉽게 인지하기 힘들 수 있다.

이를 방지 하기 위해 fail_on_pagination_over_collection_fetch을 적용하면 경고 메시지가 아니라 예외를 발생시킬 수 있다. 

jpa:
  properties:
    hibernate:
      query:
        fail_on_pagination_over_collection_fetch: true

메모리에서 페이징이 적용되려는 상황인데 해당 옵션이 활성화 되어 있다는 이유로 예외를 발생 시킨다. 

 

참고 

https://tecoble.techcourse.co.kr/post/2021-07-26-jpa-pageable/

 

JPA Pagination, 그리고 N + 1 문제

1. Pagination 게시판 기능을 제공하는 웹 어플리케이션에 접속하여 게시물 목록을 요청하는 경우를 상상해봅시다. DB…

tecoble.techcourse.co.kr

https://tecoble.techcourse.co.kr/post/2020-10-21-jpa-fetch-join-paging/

 

JPA에서 Fetch Join과 Pagination을 함께 사용할때 주의하자

결론부터 말하면, One에서 Many를 fetch join 해야하는 경우 limit과 같은 절(Pagination을 위한)을 포함할 시 원하는 대로 결과나 나오지 않는다. 예제코드(+ 테스트코드)는 Github에서 확인할 수 있다. LIMIT

tecoble.techcourse.co.kr

https://jojoldu.tistory.com/737

 

Hibernate Fetch Join시 메모리에서 페이징 처리 사전 차단하기

Hibernate (Spring Data JPA) 를 사용하다보면 종종 HHH000104: firstResult/maxResults specified with collection fetch; applying in memory! 의 WARN (경고) 로그 메세지를 만난다. 해당 로그는 페이징 처리할때 여러 엔티티를 Fe

jojoldu.tistory.com