주문 조회 V3: 엔티티를 DTO로 변환 - 페치 조인 최적화
Last updated
Last updated
앞에서는 xToOne관계에서 join fetch였지만 여기서 Order와 OrderItems, 즉 일대다를 join fetch 한다.
OrderRepository에 추가
주문 id=4인 주문 하나에 대해 orderItems는 2개이다. (문제점)그렇기 때문에 join결과는 2개가 나오고, JPA가 데이터를 가져올 때도 2개를 조회해버린다!
join 된 DB 결과 확인 : 총 row가 4개가 되버렸다. =>Order는 2건인데 join 함으로써 4개가 조회되버린 것이다. 우리는 2개의 Order를 조회하고 싶다!
디버깅 결과 참조값까지 같은 것을 알 수 있다. JPA에서 PK가 같다는 것은 완전히 같음을 의미한다.
이렇게 일대다 관계에서 join fetch을 하면 다의 갯수만큼 데이터가 늘어난다‼️⭐️⭐️⭐️⭐️⭐️ DB 입장에서는 Join fetch 도 결국 join이다. 다만 select 절에 데이터를 더 넣는지 아닌지의 차이일 뿐이다.
join함으로써 데이터가 늘어난 것인데 hibernate(또는 다른 구현체)는 원래 데이터인지, 증가된 데이터인지 모른다. 그래서 DB에 조회한 데이터를 객체 그래프로 가져올 때 기준이 필요하다.
해결 방법 : JPA distinct 사용
JPA distinct 기능
DB 에 들어가는 쿼리에 distinct 추가
root(여기서는 Order 엔티티)가 중복이면 제거하고 컬렉션에 담아준다!
하지만 DB는 한 줄이 다른 한줄과 완전히 똑같아야만 중복으로 인정되어 제거된다! 주문 번호는 같더라도 주문 상품은 각각 다 다르다! 그렇기 때문에 DB에서 조회되는 데이터는 중복 제거되지 않고, 여전히 4개의 row가 조회된다!
정리 JPA의 join fetch와 distinct를 사용하면 막강하다!
join fetch : 원하는 조회하려는 데이터(객체 그래프)를 찍어주고 distinct : root의 중복 제거
한번의 쿼리로 원하는 데이터를 조회한다! 컨트롤러상에서 V2, V3는 똑같지만 그 내부에서 동작한는 게 다르다!
‼️xToOne fetch join과 컬렉션 fetch join 다른점(유의점)
페이징 불가!!! - limit offset이 나와야하는데 안 나온다!
컬렉션 페치 조인은 1개만 사용 가능
오류 메시지 출력 일단 데이터는 조회하지만 그 데이터들을 메모리에서 페이징 처리한다! =>실무에서 데이터가 1만개라면 메모리초과 나서 큰일 난다!
일대다 join을 하는 순간 order의 기준 자체가 틀어진다! 우리가 원하는 데이터 : 2개 실제 DB의 데이터 : 4개 이므로 페이징할 경우 원하는 결과가 제대로 나오지 않는다!
우리가 원하는, 예상하는 데이터는 2개이기 때문에 0부터 2개 가져와라고 할 수 있지만 DB SQL입장에서는 페이징은 Order가 아니라 OrderItem과 조인된, 즉 OrderItem 테이블에 페이징이 적용된다!!! =>그러므로 페이징 자체가 불가능해진다! =>중간에서 갯수를 못 맞춘다! 2 vs 4
그래서 hibernate는 WARN 을 내고 메모리에서 해준다.(아주 위험‼️ 절대 사용❌) 데이터가 작은 경우는 괜찮지만, 실무에서 데이터가 많은데 해버리면 메모리 초과 발생!
따라서 일대다 관계에서는 다에 의해 데이터가 늘어나므로 페이징❌
2. 컬렉션 페치 조인은 1개만 사용할 수 있다. 컬렉션 둘 이상에 페치 조인을 사용하면 안된다. 데이터가 부정합하게 조회될 수 있다. 자세한 내용은 자바 ORM 표준 JPA 프로그래밍을 참고하자. =>데이터가 증가하는 건 물론이고, 데이터 갯수나 정합성이 안 맞을 수 있다! ex) 일대다(N) * 일대다(M) = N * M 꼴이다!