어제 공부하면서 배운 ~~toOne 관계는
Collection이 아닌 객체 한개를 조회하기때문에 fetch Join을 사용하면 큰 문제는 없었다.
이번 강의에서는 ~~toMany 관계가 포함된 Order 를 조회하면서 문제점과 해결방안에대해 알아봤다.
우선 toOne관계와 똑같이 fetch Join이 들어간 방식이 DB에 쿼리를 어떻게 전송하는지와 응답한 JSON을 살펴보자.

기존 처럼 fetch join을 이용해 get요청을 했더니 첫번째 문제가 발생했다.


JSON에 중복된 데이터가 응답됬다.
이유가 뭘까 Hibernate가 DB에 전송한 쿼리를 H2에 날려 어떤 테이블 형식을 가지는지 봤더니

1개의 Order를 조회할때 Order가 ~~toMany 관계로 가지고있는 OrderItem 2개를 join하며 테이블이 row가 orderItem 갯수만큼 늘어났다.
이러한 Data를 받은 Application은 각각의 Order가 두개씩 날라오니 자연스럽게 잭슨 라이브러리도 JSON을 중복하여 응답하게됬다.
이 문제를 해결하기위해 Select 절에 distinct를 넣어주자

JSON값이 중복되지않고 잘 넘어오게됬다.
DB에서 조회자체는 Order : OrderItem 관계가 1 : 2로 이뤄져 row는 그대로 중복되어 남아있지만
JPA에서 distinct가 존재한다면 Application에서 중복된 데이터를 제외하고 데이터를 받는다.
이렇게 여러개가 존재하는 연관관계도 fetch Join을 활용하면 N + 1 문제를 해결 할 수 있지만 이 방법에도 단점을 존재한다.
페이징처리가 안된다는점
왜 페이징 처리가 안되는것일까?
: 앞서 말했듯이 일대다 관계의 데이터 조회시 DB에서는 테이블의 일정한 Row를 유지하기 위해 중복된 Row을 생성한다. 이 과정에서 DB내에서 sorting이 불가능해지고 정렬을 Application에게 맡겨버린다.
몇개 안되는 수의 데이터의 정렬처리는 Application 메모리내에서 가능하겠지만 만약 페이징 정렬할 데이터가 만개 십만개가 된다면 메모리 과부하로 장애가 발생할것이다.
페이징 처리를 하고싶다면
default_batch_fetch_size: 를 적용하자

우선 application.yml 설정에서 글로벌하게 값을 적용해준뒤
JPA 전체 조회 쿼리의 수정이 필요하다.

fetch join은 '~~toOne'관계만 해준뒤 페이징처리를 진행할거니까 offset값과 limit값을 받아준다.
이후 페이징 조회를 해보면

첫번째 쿼리에서는 toOne 관계가 조인된 쿼리문이 나가고
우리가 필요한 콜렉션 데이터는 이렇게 IN 절이 포함된 쿼리가 따로 나가게 된다.
default_batch_fetch_size 를 적용하면 연관된 콜렉션을 IN절로 한꺼번에 가져온다.
Size 값은 배치의 크기로 Min값은 존재하지않고 Max값은 1000이다.
Size값을 1000으로 하는것이 쿼리가 적게 나가 성능상 좋지만,
DB와 Application에 과부하가 올 수있어 적당한 값을 찾아 적용하는것이 좋다.
IN절이 포함된 쿼리가 콜렉션만큼 더 나가므로 Order:OrderItem:Item = 1 : 1 : 1 비율로 쿼리가 전송된다.
'공부 > JPA' 카테고리의 다른 글
OSIV (0) | 2023.12.18 |
---|---|
JPA N + 1 과 FetchJoin (0) | 2023.12.15 |