[JPA] 페치조인과 일반 조인의 차이점

2024. 12. 27. 11:45·JPA
목차
  1. 페치조인
  2. 일반 조인 사용
  3. 페치 조인 사용
  4. 페치조인과 일반조인의 차이점

페치조인과 일반 조인의 차이점을 알아보기 앞서 먼저 페치조인에 대해 알아보자.

 

페치조인

페치조인은 SQL의 조인 종류는 아니다. JPQL의 전용기능이고,  JPQL에서 성능 최적화를 위해 제공하는 기능이다. 

한마디로 설명하자면 연관된 엔티티나 컬렉션을 하나의 SQL에 함께 조회하는 기능이다.

 

예제

// Member.java
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
private String username;
}
// Team.java
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
private String name;
@Override
public String toString() {
return "Team [id=" + id + ", name=" + name + "]";
}
}

 

예제

먼저 회원 1, 2는 TeamA에, 회원 3은 Team B에, 그리고 회원 4는 팀 소속 X으로 세팅했다.

더보기

예제를 사용한 세팅 코드

Team team1 = new Team();
team1.setName("teamA");
em.persist(team1);
Team team2 = new Team();
team2.setName("teamB");
em.persist(team2);
Member member1 = new Member();
member1.setUsername("member1");
member1.setTeam(team1);
em.persist(member1);
Member member2 = new Member();
member2.setUsername("member2");
member2.setTeam(team1);
em.persist(member2);
Member member3 = new Member();
member3.setUsername("member3");
member3.setTeam(team2);
em.persist(member3);
Member member4 = new Member();
member4.setUsername("member4");
em.persist(member4);

 

일반 조인 사용

먼저 Join만 사용해서 회원 조회를 해보자.

예제코드

String query = "select m from Member m join m.team";
List<Member> result = em.createQuery(query, Member.class).getResultList();
for (Member member : result) {
System.out.println("member.getUsername() = " + member.getUsername());
System.out.println("member.getTeam().getName() = " + member.getTeam().getName());
}

 

Member엔티티의 필드 Team 엔티티가 지연로딩 전략이므로 프록시 Team 객체를 접근할 때 마다 쿼리가 실행된다. 

Hibernate:
select
m1_0.id,
m1_0.team_id,
m1_0.username
from
member m1_0
join
team t1_0
on t1_0.id=m1_0.team_id
member.getUsername() = member1
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?
member.getTeam().getName() = teamB
member.getUsername() = member2
// 이하 생략

만약 회원 100명의 팀이 다 다르다면 100개의 쿼리가 더 실행될 것이다. 이 문제가 바로 N+1문제라고 한다. 

 

페치 조인 사용

이제 페치 조인을 사용해서 조회를 해보자.

String query = "select m from Member m join fetch m.team";
List<Member> result = em.createQuery(query, Member.class).getResultList();
for (Member member : result) {
System.out.println("member.getUsername() = " + member.getUsername());
System.out.println("member.getTeam().getName() = " + member.getTeam().getName());
}
Hibernate:
select
m1_0.id,
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
member.getUsername() = member1
member.getTeam().getName() = teamA
member.getUsername() = member2
member.getTeam().getName() = teamA
member.getUsername() = member3
member.getTeam().getName() = teamB

실행결과에서 볼 수 있듯이 페치조인으로 회원과 팀을 한번에 조회해서 N+1문제가 발생하지 않는걸 볼 수 있다. 

result에 실행결과 값이 담길 때 Team 객체는 프록시 객체가 아닌 실제 엔티티 객체가 담긴다.

여기서 알 수 있는점은 지연로딩으로 세팅해도 페치 조인이 우선이라 같이 조회된다는 점이다.

 

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

아니 그러면 Member 엔티티의 Team 엔티티 로딩 전략을 즉시로딩으로 바꾸고 페치 조인 대신 그냥 일반 조인 써도 되는거 아니야? 

결과부터 말하자면 그래도 결국 N+1 문제가 발생한다.

예제를 통해 알아 보자. 

// Member.java
@ManyToOne(fetch = FetchType.EAGER) // 즉시로딩으로 수정
@JoinColumn(name = "TEAM_ID")
private Team team;
Hibernate:
select
m1_0.id,
m1_0.team_id,
m1_0.username
from
member m1_0
join
team t1_0
on t1_0.id=m1_0.team_id
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?

join이라는 쿼리로 조회할때는 JPA에서 from절에 있는 엔티티만 조회한다. 즉 연관된 엔티티를 함께 조회하지 않는다.

Hibernate:
select
m1_0.id,
m1_0.team_id,
m1_0.username
from
member m1_0
join
team t1_0
on t1_0.id=m1_0.team_id

그래서 이 코드가 실행될 때 join으로 Team을 조회하는 것처럼 보이지만 Member 엔티티만 값을 조회후 team 객체는 프록시로 들어가 있다.

하지만 JPA가 Team의 로딩 전략이 즉시 로딩이네? 하면서 

Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?
Hibernate:
select
t1_0.id,
t1_0.name
from
team t1_0
where
t1_0.id=?

나머지 이 두 쿼리도 같이 실행한다. 결국 N+1 문제가 발생한다. 

결론적으로 

  • 일반 조인: JOIN 조건을 제외하고 FROM 절에 있는 대상 엔티티만 조회
  • 페치 조인: FROM  절에 있는 대상 엔티티와 FETCH JOIN으로 걸려 있는 엔티티를 함께 조회

 

 

 

'JPA' 카테고리의 다른 글

[JPA] JPQL의 경로표현식이란?  (0) 2024.12.27
[JPA] JPQL의 프로젝션, 페이징  (0) 2024.12.24
[JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란?  (1) 2024.12.20
[JPA] 프록시와 지연로딩, 즉시로딩  (0) 2024.11.26
[JPA] 연관관계 매핑  (0) 2024.11.25
  1. 페치조인
  2. 일반 조인 사용
  3. 페치 조인 사용
  4. 페치조인과 일반조인의 차이점
'JPA' 카테고리의 다른 글
  • [JPA] JPQL의 경로표현식이란?
  • [JPA] JPQL의 프로젝션, 페이징
  • [JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란?
  • [JPA] 프록시와 지연로딩, 즉시로딩
Economy98
Economy98
공부하고 기록하기
  • Economy98
    Economy_Dev
    Economy98
  • 전체
    오늘
    어제
    • 분류 전체보기 (74)
      • Spring Framework (11)
      • BOJ, Programmers (22)
      • Java (4)
      • JDBC (6)
      • JPA (9)
      • Spring Transaction (3)
      • Algorithm (1)
      • Web (5)
      • Projects (2)
        • 쇼핑몰 프로젝트 (0)
        • 열람실 & 도서관 프로젝트 (2)
      • Network (2)
      • 나의 공부방 (5)
      • 끄적끄적 (1)
      • Error Log (3)
      • CS (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • Github
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    자바 문제 풀이
    java
    restful api
    백준
    JPA
    스프링부트
    그리디 알고리즘
    자바 문제
    jdbc
    브루트포스 알고리즘
    다이나믹 프로그래밍
    자바
    propagation
    예외 처리
    정렬
    Spring
    백준 풀이
    트랜잭션
    스프링
    백준 자바 풀이
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
Economy98
[JPA] 페치조인과 일반 조인의 차이점

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.