1. 💡 Fetch Join
- SQL 조인의 종류가 아니다.
- JPQL에서 성능 최적화를 위해 제공하는 기능이다.
- 연관된 Entity나 Collection을 SQL 한 번에 같이 조회하는 기능 (성능 최적화)
- 글로벌 Fetch Loading 전략보다 우선순위가 높다.
- 객체 그래프를 유지할 때 사용하면 효과적이다.
- 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야하면,
일반 조인을 사용하고 필요한 데이터만 조회해서 DTO로 반환하는 것이 좋다. - join fetch 명령어 사용
- ::= [ LEFT [OUTER] | INNER ] join fetch 경로
1.1. Entity Fetch Join
엔티티를 조회하면서 연관된 엔티티까지 같이 한번의 쿼리로 조회한다.
JPQL
<code />
String query = "select m from Member m join fetch m.team";
SQL
<code />
SELECT M.*, T.*
FROM Member M
INNER JOIN Team T
ON M.team_ID = T.ID
1.2. Collection Fetch Join
주로 1:N 관계에서 컬렉션을 가져올 때 사용한다.
중복된 ID값이 있을겅우 distinct를 사용해 컬렉션에서 중복을 제거하지 않으면
보통 원래 데이터보다 더 큰 결과집합이 반환된다.
JPQL
<code />
String query = "select distinct t from Team t join fetch t.members where t.name = 'teamA'";
SQL
<code />
SELECT T.*, M.*
FROM Team T
INNER JOIN Member M
ON T.ID = M.Team_ID
WHERE T.name = 'teamA';
1.3. 한계
- fetch join 대상은 왠만하면 Alias 부여를 하지 않는게 좋다. (데이터 정합성의 이유)
- 둘 이상의 컬렉션은 Fetch Join을 할 수 없다.
- 컬렉션을 Fetch Join하면 페이징 API를 사용할 수 없다
- setFirstResult, setMaxResults 등 사용불가능
- 1:1, N:1 같은 단일 값 연관 필드들은 Fetch Join시 페이징 API를 사용 가능하다.
- Hibernate는 경고 로그를 남기고 메모리에서 페이징한다. (매우 위험)
1.4. 컬렉션 페이징 API 적용
fetch join으로 해결이 불가능한, 컬렉션에 페이징을 적용하려면 BatchSize를 활용한다.
@BatchSize(size = 100), 값은 1000 이하의 적절한 값으로 설정한다.
yml 설정은 hibernate.default_batch_fetch_size의 value도 설정해주면 된다.
이렇게 Batch Size를 설정하면 N+1 쿼리가 아닌 테이블의 수 만큼 쿼리를 맟출 수 있다.
1.5. 생각해보기
객체 그래프라는건 기본적으로 연관된 데이터를 전부 가져오는것이다.
하지만 fetch join을 하고 조건을 걸어 전체가 아닌 일부만 가져오는것보다,
그냥 직접 그 데이터들을 가져오는 쿼리를 작성하는게 더 낫다.
그리고, 단일 값 연관 필드가 아닌 컬렉션 값 연관 필드처럼 여러개의 값을 가진 쿼리의
페이징 API가 안되는 이유를 생각해보자.
간단한 예시로 팀이 "A"인 회원 2명을 조회하고 총 2페이지에 페이지당 1건을 출력한다고 가정한다.
그럼 2건의 반환 결과 중 한 페이지에 한개의 데이터만 들어가고 다른 하나가 누락되며,
반환결과에 팀 "A"는 회원을 하나만 가지고 있게 되는 셈이다.
'Database > JPQL' 카테고리의 다른 글
JPQL 벌크 연산 & 다형성 쿼리 & Named 쿼리 (0) | 2023.03.22 |
---|---|
JQPL 경로표현식 (2) | 2023.03.19 |
JPQL Type Query & Sub Query (0) | 2023.03.11 |
JPQL Paging API & Search Result API (0) | 2023.03.11 |
JPQL Projection (0) | 2023.03.11 |