TIL/김영한의 자바 ORM 표준 JPA 프로그래밍 - 기본편

객체지향 쿼리 언어 - 페치 조인

minOE 2025. 7. 10. 14:51
728x90

페치 조인(fetch join)

- SQL 조인 종류 X
- JPQL에서 성능 최적화를 위해 제공하는 기능
- 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능
- join fetch 명령어 사용
- 페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로

 

엔티티 페치 조인

- 회원을 조회하면서 연관된 팀도 함께 조회(SQL 한 번에)
- 다음 JPQL은 Hibernate 6에서 실행 시 아래와 같은 SQL로 변환됩니다
-- JPQL
select m from Member m join fetch m.team

-- Hibernate 6에서 변환된 SQL (로깅 기준)
select
    m1_0.id,
    m1_0.age,
    t1_0.id,
    t1_0.name,
    m1_0.username 
from
    Member m1_0 
join
    Team t1_0 on t1_0.id = m1_0.TEAM_ID​


 

 

컬렉션 페치 조인과 DISTINCT

- 일대다 관계, 컬렉션 페치 조인
- 다음 JPQL은 Hibernate 6에서 실행 시 아래와 같은 SQL로 변환됩니다
-- JPQL
select distinct t 
from Team t 
join fetch t.members 
where t.name = '팀A'

-- Hibernate 6 SQL (로그 출력 기준)
select
    distinct t1_0.id,
    m1_0.TEAM_ID,
    m1_0.id,
    m1_0.age,
    m1_0.username,
    t1_0.name 
from
    Team t1_0 
join
    Member m1_0 
    on t1_0.id = m1_0.TEAM_ID 
where
    t1_0.name = '팀A'​


- SQL DISTINCT 중복된 결과를 제거하는 명령
-
JPQL DISTINCT 2가지 기능 제공
    1)
SQL DISTINCT 추가
    2)
애플리케이션에서 엔티티 중복 제거

 

페치 조인과 일반 조인의 차이


1. 일반 조인 (join)
-- JPQL
select distinct t 
from Team t join t.members m 
where t.name = '팀B'



-- Hibernate 실행 SQL (쿼리 1)
select
    distinct t1_0.id,
    t1_0.name 
from
    Team t1_0 
join
    Member m1_0 on t1_0.id = m1_0.TEAM_ID 
where
    t1_0.name = '팀B'

-- 추가 SQL (getMembers() 호출 시 지연 로딩 발생)
select
    m1_0.TEAM_ID,
    m1_0.id,
    m1_0.age,
    m1_0.username 
from
    Member m1_0 
where
    m1_0.TEAM_ID = ?

- Team만 select 대상이므로, members는 로딩되지 않음
- team.getMembers() 호출 시점에 추가 SQL 실행됨 (지연 로딩, Lazy)
- 쿼리 2번 실행됨 (N+1 가능성 있음)



2. 페치 조인 (join fetch)
-- JPQL
select distinct t 
from Team t join fetch t.members m 
where t.name = '팀B'

-- Hibernate 실행 SQL (쿼리 1회)
select
    distinct t1_0.id,
    m1_0.TEAM_ID,
    m1_0.id,
    m1_0.age,
    m1_0.username,
    t1_0.name 
from
    Team t1_0 
join
    Member m1_0 on t1_0.id = m1_0.TEAM_ID 
where
    t1_0.name = '팀B'​

- Team과 연관된 Member를 한 번의 SQL로 모두 로딩
- getMembers() 호출해도 추가 쿼리 발생하지 않음
- 성능 효율적 , N+1 방지


✅  정리 비교
페치 조인은 연관 엔티티까지 한 번에 가져와 N+1 문제를 해결하고 성능을 최적화할 수 있습니다.
일반 조인은 연관 객체를 즉시 로딩하지 않기 때문에, 연관 데이터를 사용할 경우추가 쿼리가 발생할 수 있습니다.
출력은 같아 보여도 쿼리 수와 성능은 다릅니다.


728x90