※ 페치 조인은 대부분의 'N+1' 문제를 해결 할 수 있다.
※ Member 엔티티 / Team 엔티티
0. 필드 (field) 종류
① 상태필드 (state field) : 단순히 값을 저장하기 위한 필드
* 연관필드 (association field) : 연관관계를 위한 필드
② 단일 값 연관필드
: @ManyTo"One", @OneTo"One" 연관관계에 해당하며, 대상이 '엔티티' 이다. (ex) m.team)
③ 컬렉션 값 연관필드
: @OneTo"Many", @ManyTo"Many" 연관관계에 해당하며, 대상이 '컬렉션' 이다. (ex) m.order)
※ 컬렉션 값 연관필드의 탐색
컬렉션 값 연관필드(t.members) 에서 한번더 탐색(.username) 이 불가능하다.
만약, 탐색을 원한하면, 명시적 조인을 통해 컬렉션에 별칭을 부여하고, 그 별칭을 통해 탐색한다.
중요!) "명시적 조인"을 권장한다.
'묵시적 조인'은 편하고 좋아 보일 수 있지만, 쿼리문만 보고 SQL 문을 판단하기 어려워 유지보수의 문제가 있을 수 있다.
1. 페치 조인 (Fetch Join) 소개
'페치 조인' 은 JPQL 에서 성능 최적화를 위해 제공하는 기능이다.
이는 JPQL의 전용 기술로서, SQL 조인 종류가 아니다.
'페치 조인' 은 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능이다.
"join fetch" 명령어를 사용한다.
- '페치 조인'은 '일반 조인'과 어떻게 다른가?
: 일반 조인 실행시 연관된 엔티티를 함께 조회하지 않는다.
때문에, 조인 대상 엔티티의 값을 요청할때, SQL 문을 새로 보낸다. (지연로딩 처럼 적용)
반면, 페치 조인 실행시 연관된 엔티티(페치 조인 대상)를 함께 조회한다.
때문에, 페치 조인 대상 엔티티의 값을 요청하더라도, 이미 로딩이 되었기 때문에 즉시 값을 반환한다. (즉시 로딩 효과)
=> 페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념이다.
2. 엔티티 페치 조인
* "엔티티 페치 조인"을 사용하는 경우
※ 중요!) team은 지연로딩을 설정했다 해도 페치 조인 설정이 먼저 적용된다.
* "엔티티 일반 조인"을 사용하는 경우
※ 페치 조인을 사용하는 경우 - 즉시 로딩의 효과
(원하는 객체를 원하는 시점에 즉시 로딩 하는 효과가 있다.)
3. 컬렉션 페치 조인
* "컬렉션 페치 조인"을 사용하는 경우
** 데이터 뻥튀기 현상
DB 입장에서 일대다 조인을 하면 데이터가 뻥튀기가 된다. -> JPA 도 마찬가지
1. result 는 members 를 가지고 있는 team 객체를 담는 리스트 로,
'team1' 객체가 2번 중복으로 들어가게 된다.
2. 반복문으로 돌리게 되면, 당연하게도 데이터가 중복으로 조회되게 된다.
이와 같은 현상을 "데이터 뻥튀기" 라 칭하겠다.
* 데이터 뻥튀기 (중복)을 제거하려면?
** DISTINCT ** 명령어 사용
SQL 문 처럼 JPQL에서도 'DISTINCT' 가 중복을 제거하기 위해 존재한다.
그러나 SQL과 JPQL에서의 DISTINCT는 살짝 다르다.
JPQL's DISTINCT는 2가지 기능을 동시에 가지고 있다.
1) SQL에 DISTINCT를 추가
: DB에서 가져올때, 데이터 중복 제거
2) 애플리케이션에서 엔티티 중복 제거
: 애플리케이션에 올린 후, 데이터 중복 제거
4. 페치 조인의 특징과 한계
1. 페치 조인은 연관된 엔티티를 함께 조회한다.
2. 페치 조인 대상에는 별칭을 줄 수 없다.
: 만약, 페치 조인으로 모든 데이터를 가져올 필요가 없고 일부 데이터만 원한다고 하자.
이 경우 페치 조인 대상에 별칭을 주고, 별칭을 통해 조건문을 달아서 일부만 가져온다면, 어떠한 문제가 발생할까?
-> 이는 원치 않는 데이터 누락 발생 가능성이 높아진다. 이 방법을 사용하는 것은 페치 조인의 설계 목적에도 맞지 않으며, 데이터 정합성의 문제도 야기할 수 있다.
3. 둘 이상의 컬렉션은 페치 조인 할 수 없다.
: @OneToMany 연관관계 는 '데이터 뻥튀기' 현상을 발생시킨다.
그런데, 여기서 둘 이상의 컬렉션을 페치 조인한다면, '데이터 뻥튀기 X 데이터 뻥튀기' 로 엄청난 데이터 양 증가가 발생할 가능성이 생기며, 이를 예측하는 것은 매우 어렵다.
4. 컬렉션을 페치 조인하면 페이징 API(setFirstResult, setMaxResults)를 사용할 수 없다.
: @OneToMany 연관관계 는 '데이터 뻥튀기' 현상을 발생시킨다.
'데이터 뻥튀기' 는 데이터의 양을 예측할 수 없다.
그렇기에 페이징 API를 사용한다면, 모든 겂이 페이징 되는 것이 아닌 중간에 뚝 잘려 나올 수 있는 불완전한 값을 받을 위험이 있다.
페이징 과정은 메모리에 페이징 대상의 데이터를 모두 올린 후 페이징 작업을 진행한다.
여기서 데이터 뻥튀기 된 데이터 양이 모두 메모리에 올라간다면, 심각한 메모리 과부화로 이어질 수 있다.
'Spring > JPA' 카테고리의 다른 글
[JPA - 10] 즉시로딩 / 지연로딩 (0) | 2023.12.07 |
---|---|
[JPA - 9] 프록시 (Proxy) (0) | 2023.12.05 |
[JPA - 8] 상속관계 매핑 / @MappedSuperclass (0) | 2023.12.02 |
[JPA - 7] 다양한 연관관계 매핑 (0) | 2023.12.01 |
[JPA - 6] 연관관계 매핑 (양방향 연관관계 / 연관관계의 주인 - 중점) (0) | 2023.11.26 |