본문 바로가기

강의 내용 정리/스프링부트와 JPA 활용 2

4-3. 주문 조회 V3: 엔티티를 DTO로 변환 - 페치 조인 최적화

 

join fetch 1,2 번째 줄은 XToOne 이므로 데이터뻥튀기 될 가능성이 없다.

하지만 join fetch 3,4번째 줄은 XToMany 이므로 조회되는 데이터 뻥튀기 될 가능성이 있다. (컬렉션 LAZY 조회)

 

 

join은 1,2 가 조인하면 2개가 된다.

 

ㄴorder 2개, orderitem 4개 조인하면 order가 4개가 된다.

이 이미지를 보면 orderitem에서 FK인 order_id로 join하였는데 RDB에서는 맨 밑처럼 두 줄로 표현 될 수 밖에 없다.

(order 2개, orderitem 4개면 결과적으로 4개를 끌어와야함.)

 

-> 이렇게 되면 문제 : JPA에서 order를 가져올 때 데이터가 두배가 되어버린다.

 

진짜 order 1,1,2,2 총 4개가 나온다.

쿼리를 그대로 쳐봐도 4개가 나온다.

DB입장에서 join을 해버리면 1:다 개 조인하면 다 개만큼 데이터가 뻥튀기 된다.

DB입장에서 fetch join 도 join이다. -> 대신 select 절에 데이터를 넣어주냐마냐의 차이.

 

문제: 뻥튀기 된 것을 하이버네이트 입장에서는 모른다. -> 이걸 써야하나 말아야하나

(우린지금 order에 대해선 뻥튀기 하고 싶지 않다.)

 

JPA에서는 PK가 같으면 완전 같은 객체

 

distinct를 넣어라. (SQL의 distinct + a 기능.)
실제 쿼리에도 distinct를 넣어준 것을 볼 수 있다.

하지만

이건 아직 4개가 나온다.

-> sql의 distinct는 진짜 row 전체가 완전히 같아야 중복제거 해준다. (근데 보면 알 듯이 뭔가 다르다. (DB쿼리에서는 중복제거가 안된다.))

 

 

-> 하지만 JPA에서는 자체적으로

distinct 가 있으면 order를 가져올 때 이 order가 같은 id(PK)값이면 중복을 제거해줌. (하나를 버림)

 

JPA에서 distinct 키워드의 기능:

1. DB에 SQL의 distinct 키워드를 날림

2. PK기준으로 엔티티가 중복일때 중복제거 후 반환해준다.

 

지금까지 이런 고민을 한 이유 : XToMany 였기 때문에

--------

 

 

객체 그래프 탐색을 위해 페치조인을 했다.

 

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

JPA를 사용하면 객체그래프탐색 원하는 객체만 이렇게 찍어주면 알아서 SELECT에 넣어서 쿼리 한번으로 반환해줌

 

치명적인 단점 : 페이징 불가능

1대다를 페치조인하는 순간 페이징 쿼리가 아예 안나감.

 

이런식으로 페이징이 안됨.

쿼리를 봐도 페이징 관련 쿼리가 없다!!

 

하이버네이트가 경고해줌 : 너희가 페치조인을 썼는데 페이징 쿼리가 추가되면 메모리에서 페이징처리가됨.

만약 데이터 10000개면 이 10000개를 애플리케이션에 퍼올려서 메모리에서 페이징함-> 쉽게 작살남

 

그럼 왜 하이버네이트는 이렇게 설정되었을까

-> 위에서 보았듯이 일대다 조인 하는 순간 order가 4개가 됨 -> 다 를 기준으로 데이터가 뻥튀기 되어버리니까 DB에서 페이징이 불가해짐 (만약 페이지쿼리를 실제로 날려서 DB에서 페이징 하게 되면 다 쪽인 orderitem을 기준으로 페이징이 되어버림.) -> 그래서 그냥 메모리에서 해줌. (메모리에선 가능한 이유 : JPA의 distinct 로 PK가 같은 객체는 중복제거했기 때문)(데이터 적을 땐 괜찮지만 많아지면 굉장히 위험함.) 

 

-> 물론 XToOne인 member, delivery, item은 페치조인쓸 때도 페이징해도됨.

-> 그러나 XToMany인 orderitem은 안됨.(위와 같은 이유로.)

 

 

 

--페이징 관련 이해 안되면 4-3 20분부터.

 

컬렉션 페치 조인은 하나만! (1:다:다...) 가 되면 데이터 부정합하게 조회 될 수 있음.

뭐 페이징 안쓸거면 컬렉션(1대다)페치조인 해도된다.

 

 

 

=====

추가로 생각해볼 부분.

 

여기서 List<Order> orders 는 영속상태일까? 트랜잭션은 언제 열리고 닫힐까?

1. order 두개는 영속상태이다.

2. em.createQuery()를 하는 과정에서 DB와 통신을 하기 위해 트랜잭션이 열고 닫히게 됩니다.

 

그럼 어떻게 order가 영속상태이지?? -> 기본적으로 스프링은 OSIV가 켜져있고, OSIV가 켜져있으면 영속성 컨텍스트와 DB 커넥션이 API 응답이 끝날 때까지 살아있다.

-> 트랜잭션은 em.createQuery()에서 열고닫히지만 OSIV로 인해 영속성컨텍스트는 API응답 끝 까지 살아있다.

-> 그러므로 우리는 order에서 orderiem, item등을 LAZY로딩하였다.

 

Recent Posts
Popular Posts
Recent Comments