fetch join은JPA에서 N+1 문제를 해결하고, 연관된 엔티티를 한 번의 쿼리로 효율적으로 조회하기 위해 사용하는 기능이다.
🔹 일반 JOIN: 연관된 엔티티를 가져오지만, 조회된 엔티티는 지연 로딩(Lazy Loading)이 적용되어 필요할 때 쿼리가 추가로 발생한다
🔹 FETCH JOIN: 연관된 엔티티를 즉시 로딩(Eager Loading)으로 가져오며, 한 번의 SQL로 연괸된 모든 데이터를 조회한다
SELECT m FROM Member m JOIN FETCH m.team;
🔹 JOIN FETCH를 사용하면 Member와 Team을 한 번의 쿼리로 가져온다
🔹 JOIN만 사용하면 Member만 즉시 로딩되고 Team은 지연로딩된다
N+1 문제는 데이터베이스와 연동된 애플리케이션에서 발생하는 성능 문제 중 하나로, 하나의 쿼리를 실행할 때 추가로 N개의 쿼리가 더 실행되는 현상을 말한다
연관된 엔티티를 조회할 때 지연 로딩(Lazy Loading) 방식으로 데이터를 가져올 경우 발생한다
List<Post> posts = postRepository.findAll();
for(Post post : posts){
System.out.println(post.getComments().size();
}
🔹 postRepository.findAll() ➡️ 게시글을 조회하는 1개의 쿼리 실행
🔹 post.getComments() ➡️ 각 게시글의 댓글을 조회하는 N개의 쿼리 실행
✨ 따라서 게시글 10개를 조회하면 1 + 10 = 11번의 쿼리가 실행된다
🔹 성능 저하: 불필요한 쿼리가 다수 발생해 데이터베이스 부하 증가
🔹 네트워크 오버헤드: 쿼리 실행 횟수 증가로 인한 응답 시간 지연
🔹 트래픽 증가: 애플리케이션과 DB 서버 간의 트래픽 과부하
예제 코드
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
}
JOIN
public List<Member> findMembers() {
return em.createQuery("SELECT m FROM Member m JOIN m.team", Member.class)
.getResultList();
}
🔹 Member를 조회한 후 Team을 지연로딩으로 가져온다
🔹 회원이 100명이면 1+100번의 쿼리가 실행된다(N+1 문제 발생)
FETCH JOINN
public List<Member> findMembersWithTeam() {
return em.createQuery("SELECT m FROM Member m JOIN FETCH m.team", Member.class)
.getResultList();
}
🔹 Member와 Team을 한 번의 쿼리로 모두 가져온다
🔹 성능 최적화에 유리하다
FETCH JOIN의 주의점🔹fetch join은 복잡한 쿼리에서 과도하게 사용하면 성능 저하를 초래할 수 있으므로 적절하게 활용해야 한다
🔹fetch join 대상에는 별칭을 줄 수 없다 (JPA 표준에서는 금지, Hibernate에서는 가능하지만 권장X)
🔹 둘 이상의 컬렉션은 fetch join이 불가능하다
🔹 컬렉션을 fetch join 하면 페이징 API를 사용할 수 없다(일대다 관계에서)
🔹 fetch join은 JPA에서 N+1 문제를 해결하기 위한 강력한 도구이다
🔹 연관된 엔티티를 한 번의 쿼리로 가져오므로 성능 최적화에 유리하다