실험 배경

<aside> 🚀

Member 수 : 3

Member 엔티티에 Team과 Major를 필드로 가지고 있음 ( ManyToOne 매핑 )

적은 Member 수 안에서 가장 극단적인 쿼리 수 차이를 유도하고자 모든 Team과 Major는 모두 고유하도록 함

</aside>

LAZY + findAll() (no access)


=============== findAll (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Member만 가져오고 Team과 Major는 프록시 상태 그대로 둠
추가적인 연관 엔티티 접근이 없기 때문에 추가 SQL이 없음
그래서 N + 1 문제 발생 안함

LAZY + jpql (no access)

SELECT m FROM Member m
=============== JPQL (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
findAll() 상황과 동일
Member만 조회하기 때문에 N + 1 발생하지 않음

LAZY + fetch (no access)

SELECT m FROM Member m JOIN FETCH m.team JOIN FETCH m.major
=============== Fetch Join (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m2_0.id,
        m2_0.name,
        m1_0.name,
        t1_0.id,
        t1_0.name 
    from
        member m1_0 
    join
        team t1_0 
            on t1_0.id=m1_0.team_id 
    join
        major m2_0 
            on m2_0.id=m1_0.major_id
이후 getter로 인해 Team과 Major를 호출할지 하지 않을지 고려하지 않고 일단 관련 테이블을
모두 조인함
N + 1 발생하지 않음

LAZY + findAll() (with access)


=============== findAll (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?

최초에 Member를 가져오고 Team과 Major를 프록시로 둠
이후 DTO 변환 과정에서 getTeam().getName(), getMajor().getName() 호출
N + 1 발생 (1 + 3 + 3 = 7번)

LAZY + jpql (with access)

SELECT m FROM Member m
=============== JPQL (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
findAll()과 접근 동일
Member만 가져왔으나 이후 Team과 Major의 getter에 의해 실제 엔티티 조회하게 됨
N + 1 발생

LAZY + fetch (with access)

SELECT m FROM Member m JOIN FETCH m.team JOIN FETCH m.major
=============== Fetch Join (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m2_0.id,
        m2_0.name,
        m1_0.name,
        t1_0.id,
        t1_0.name 
    from
        member m1_0 
    join
        team t1_0 
            on t1_0.id=m1_0.team_id 
    join
        major m2_0 
            on m2_0.id=m1_0.major_id
no access와 같은 이유로 N + 1 발생하지 않음
최초에 Team과 Major를 프록시가 아닌 실제 엔티티로 영속성 컨텍스트에 등록함

EAGER + findAll() (no access)


=============== findAll (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
EAGER는 항상 즉시 로딩함
Member만 가져와도 연관 엔티티 Team과 Major를 추가 조회함
N + 1 발생

EAGER + jpql (no access)

SELECT m FROM Member m
=============== JPQL (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
findAll과 똑같이 동작
N + 1 발생

EAGER + fetch (no access)

SELECT m FROM Member m JOIN FETCH m.team JOIN FETCH m.major
=============== Fetch Join (연관 접근 X) ===============
Hibernate: 
    select
        m1_0.id,
        m2_0.id,
        m2_0.name,
        m1_0.name,
        t1_0.id,
        t1_0.name 
    from
        member m1_0 
    join
        team t1_0 
            on t1_0.id=m1_0.team_id 
    join
        major m2_0 
            on m2_0.id=m1_0.major_id
EAGER/LAZY를 구분하지 않고 일단 관련 엔티티를 모두 조인
N + 1 발생하지 않음

EAGER + findAll() (with access)


=============== findAll (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
no access와 같은 이유로 N + 1 발생

EAGER + jpql (with access)

SELECT m FROM Member m
=============== JPQL (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m1_0.major_id,
        m1_0.name,
        m1_0.team_id 
    from
        member m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?
Hibernate: 
    select
        m1_0.id,
        m1_0.name 
    from
        major m1_0 
    where
        m1_0.id=?
Hibernate: 
    select
        t1_0.id,
        t1_0.name 
    from
        team t1_0 
    where
        t1_0.id=?

findAll과 똑같이 동작
N + 1 발생

EAGER + fetch (with access)

SELECT m FROM Member m JOIN FETCH m.team JOIN FETCH m.major
=============== Fetch Join (연관 접근 O) ===============
Hibernate: 
    select
        m1_0.id,
        m2_0.id,
        m2_0.name,
        m1_0.name,
        t1_0.id,
        t1_0.name 
    from
        member m1_0 
    join
        team t1_0 
            on t1_0.id=m1_0.team_id 
    join
        major m2_0 
            on m2_0.id=m1_0.major_id
no access와 같은 이유로 N + 1 발생하지 않음

실험 결과 요약

조회 방법 Fetch 전략 연관 접근 여부 실행 쿼리 수
findAll() LAZY 1
findAll() LAZY 1 + N(3) + N(3) = 7
findAll() EAGER 1 + N(3) + N(3) = 7
findAll() EAGER 1 + N(3) + N(3) = 7
JPQL (SELECT m) LAZY 1
JPQL (SELECT m) LAZY 1 + N(3) + N(3) = 7
JPQL (SELECT m) EAGER 1 + N(3) + N(3) = 7
JPQL (SELECT m) EAGER 1 + N(3) + N(3) = 7
JPQL + Fetch Join LAZY 1
JPQL + Fetch Join LAZY 1
JPQL + Fetch Join EAGER 1
JPQL + Fetch Join EAGER 1