Fetch Join
JPQL에서 연관 Entity를 한 SQL로 함께 조회해 지연 로딩의 추가 Query를 제어한다.
이 문서의 목차
Overview
Fetch Join은 JPQL에서 조회 대상 Entity와 필요한 연관 Entity를 같은 SQL로 가져오는 기능이다. Entity Mapping의 기본 Fetch 전략을 바꾸지 않고 특정 Use Case의 조회 모양을 지정한다.
왜 필요한가
지연 로딩된 연관관계를 반복문에서 접근하면 최초 Entity Query 다음에 연관 Entity Query가 반복되는 N+1 문제가 생길 수 있다. Fetch Join은 필요한 관계가 명확한 조회에서 왕복 횟수를 줄인다.
핵심 원리
select m from Member m join fetch m.teamJPA는 Member와 Team 열이 결합된 SQL 결과를 읽고 Persistence Context의 식별자 기준으로 Entity를 구성한다. To-one 관계는 결과 행 수가 크게 늘지 않지만, To-many Collection을 Fetch Join하면 부모 한 건이 자식 수만큼 반복된다.
flowchart LR; A[JPQL join fetch] --> B[단일 SQL]; B --> C[반복된 Result Row]; C --> D[Persistence Context]; D --> E[Entity Graph 구성]
Example
List<Member> members = entityManager.createQuery(
"select m from Member m join fetch m.team",
Member.class
).getResultList();select distinct t from Team t join fetch t.members는 부모 Entity 중복을 줄이는 의도를 표현한다. 다만 SQL 결과의 행 수와 전송량까지 사라진다고 이해하면 안 된다.
Collection Fetch Join과 Pagination
Collection Fetch Join은 한 부모가 여러 Row로 증폭되므로 DB 수준 Pagination과 충돌한다. Page 조회에서는 To-one을 Fetch Join하고 Collection은 Batch Fetch Size, 별도 Query, DTO Projection 등을 비교한다.
실무에서 발생하는 문제
- 둘 이상의 Bag Collection을 동시에 Fetch Join하면 Hibernate 제약을 만날 수 있다.
- Collection Fetch Join과 Pagination을 같이 사용하면 Memory에서 잘라내거나 경고가 발생할 수 있다.
- 필요하지 않은 연관관계까지 한 번에 가져오면 Row 폭과 Network 비용이 증가한다.
distinct를 붙였다는 이유로 실행계획과 데이터 전송 비용을 확인하지 않는 실수가 생긴다.
Trade-off
Query 횟수는 줄지만 Join 결과 크기가 커질 수 있다. “한 번의 SQL”보다 총 Row 수, Column 폭, 사용 목적, Pagination 여부를 함께 판단한다.
Interview Questions
- 일반 Join과 Fetch Join은 JPA에서 어떤 차이가 있는가?
- Collection Fetch Join에서 결과 Row가 증가하는 이유는 무엇인가?
- Collection Fetch Join과 Pagination을 함께 사용하기 어려운 이유는 무엇인가?
- N+1을 해결할 때 Fetch Join 외에 어떤 선택지가 있는가?
Related Topics
Lazy Loading과 N+1에서 문제 발생 조건을, Persistence Context에서 Entity 동일성 구성을 함께 확인한다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.