여태까지 join보다 fetch join이 더 성능적으로 좋다고 해서 명확한 차이를 이해하지 못한채로 무작정 사용했습니다.

이번 기회에 join과 fetch join의 명확한 차이를 짚고가기 위해, 글을 작성했습니다. 

 

이전 게시글에서 지연로딩과 즉시로딩에 대해 설명했습니다.

지연로딩에서도 피할 수 없는 N+1문제를 해결하고자 fetch join 개념이 나왔습니다.

 

먼저 N+1문제가 나오는 원인에 대해 한 문장으로 옮긴다면 다음과 같이 말할 수 있습니다.

JPA가 자동으로 먼저 생성해주는 Jpql을 통해서 우선적으로 쿼리를 만들다보니 연관관계가 걸려있어도 join이 바로 걸리지 않는다.

이는 근본 원인이자,  일반 join의 기능입니다. 

일반 join을 사용하게 된다면 추가적인 쿼리가 생성이 되고, 바로 N+1 문제가 되는 것입니다. 

 

join 방식을 다르게 한 jpql문을 예시로 들어보겠습니다.

1. 일반 join문

@Query("SELECT distinct t FROM Team t join t.members")

2. Fetch join문

@Query("SELECT distinct t FROM Team t join fetch t.members")

일반 join의 경우에는 member를 join 조건으로만 사용합니다. 실제 가져오는 데이터는 Team 테이블을 대상으로만 가져오게 됩니다.

fetch join의 경우에는 member를 join조건으로 사용하면서, member 테이블의 필요한 데이터와 team 테이블의 데이터를 같이 가져옵니다.

 

JPA측면에서 다시 join방식의 차이를 이야기해보자면,

  • 일반 Join: 실제 쿼리에서 SELECT 하는 Entity는 오직 JPQL에서 조회하는 주체가 되는 Entity만 조회하여 영속화 (team만 영속화)
  • Fetch Join: 일반 join과 달리, join이 걸리는 연관 Entity도 함께 select하여 모두 영속화(team, member 영속화)

라고 말할 수 있습니다.

 

Fetch join이 걸린 Entity를 모두 영속화하기 때문에 fetchType이 Lazy인 Entity를 참조하더라도 이미 영속성 컨텍스트에 들어있기 때문에 따로 쿼리가 실행되지 않고 참조할 수 있게 됩니다.