# 페치조인의 기본

# 페치조인의 기본

fetch join은 말 그대로 조인을 할 때 조인되는 엔티티를 모두 가져오는 것을 의미한다.
이로써 성능을 최적화할 수 도 있게 되는 중요한 기술중에 하나이다.

# fetch join 왜 사용할까?

select m from member m join m.team t와 같이 member와 team을 일반조인한다고 생각하자. 그리고 각 맴버별로 팀의 이름을 출력해보도록 하는 로직을 구현하였다고 생각하자. 만약 지연로딩을 사용한다면, team은 프록시로 가지고 있을 것이다. 그러면 위 member를 부르는 sql 1개와 선택되지 않은 팀을 부르는 sql n개가 발생하게 된다. 만약 최악의 상황으로 모든 사람들의 team이 다르다면? 위 로직을 지연로딩으로 실행할 경우 member의 수가 n이라면 총 n + 1번(n번의 team조회, 1번의 member 조회)의 sql 조회가 발생하게 된다.

이러한 문제를 해결하기 위하여 fetch join을 사용하여 team과 member를 함께 가져오게 하는 것이다.
select m from member m fetch join m.team t로 사용하게 된다. 이렇게 쓰고 위 로직을 구사하면 1번의 sql의 실행만으로 해결할 수 있다. 참고로 이 sql이 실행되면 m과 t를 inner join한 결과를 얻게 된다.(영속성으로 관리)

공부를 하다보니 이 페치조인은 EAGER(즉시로딩)을 사용하는 것과 비슷하다고 느꼈다.

# collection에서 fetch join을 사용하면?

위 예시에서는 ManyToOne일 때이었고, 만약 OneToMany의 상황일 때는 어떻게 될까? select t from team t fetch join t.members m을 수행하게 되면 중복의 문제가 발생한다. team 하나에 여러개의 member가 올 수 있다. 따라서 두개를 조인하게 되면 db에서는 모든 값들을 조인해서 보여주기 때문에 각 맴버당 겹치는 team을 조회해서 보내주게 된다. 결론적으로 중복적은 team이 많아진다.

# distinct로 해결하자.

이렇게 collection에서 발생하는 중복은 distinct를 select절에서 넣어서 해결해주면 된다. distinct는 두가지 역할을 수행한다. 첫번째로 동일한 로우를 지워주고, 두번째로는 동일한 엔티티를 지워준다. 동일한 로우를 지워주는 역할은 이 문제를 해결할 수는 없다. 왜냐하면 그 경우는 team의 칼럼과 member의 칼럼이 모두 일치하는 값을 삭제해주는 것이기 때문이다.(team은 중복이지만, member가 다르기 때문에) 따라서, 이 경우 동일한 엔티티를 지워주는 이유때문에 distinct를 사용할 수 있게 되는 것이라고 이해할 수 있다. select distinct t from team t fetch join t.members m으로 수행하면 된다.

# 배우며 느낀 점

N+1문제는 LAZY에서든 EAGER에서든 어디서든 발생하는 것 같다. 이러한 문제를 해결하기 위해서는 적절히 페치조인을 섞어서 사용하여 되도록이면 LAZY를 사용하되 조회를 많이해야하는 등의 로직에서는 fetch join을 사용하여 EAGER을 사용하면 될 것 같다. 그렇게 쓸 수 있는 이유는 지연로딩 혹은 즉시로딩을 설정해주는 것보다 fetch join이 우선적으로 수행되기 때문이다.