이번 시간에는 join에 대해서 알아보자.
querydsl에서는 다음과 같은 조인을 지원한다.
join inner join, left join, right join

일단 차례대로 한번씩 살펴보자.

join

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .join(account.orders, order)
  .fetch();

join이라는 함수를 쓰면 join을 한다. join과 innerjoin의 차이점은 잘 모르겠다. 동일하게 작동하는 듯한데..
쿼리를 보자.

select 
  account0_.account_id as account_1_0_, 
  account0_.email as email2_0_, 
  account0_.name as name3_0_, 
  account0_.password as password4_0_ 
from 
  account account0_ 
inner join 
  orders orders1_ 
on 
  account0_.account_id=orders1_.account_id

쿼리에 나와 있듯이 innerjoin을 실행하였다. join과 innerjoin의 차이는 잘 모르니 패스
근데 on 메서드는 사용하지 않아도 자동으로 on은 자동으로 들어 가는 듯하다.

inner join

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .innerJoin(account.orders, order)
  .fetch();

innerJoin이라는 함수를 쓰면 innerjoin을 한다. 쿼리를 보면 더 쉽게 알수 있을 거 같다.

select 
  account0_.account_id as account_1_0_, 
  account0_.email as email2_0_, 
  account0_.name as name3_0_, 
  account0_.password as password4_0_ 
from 
  account account0_ 
inner join 
  orders orders1_ 
on 
  account0_.account_id=orders1_.account_id

쿼리에 나와 있듯이 innerjoin을 실행하였다. 마찬가지로 on메서드는 사용하지 않아도 자동으로 on은 들어 가는 듯하다.

left join

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .leftJoin(account.orders, order)
  .fetch();

leftjoin도 마찬가지로 leftJoin을 사용하면 된다. 쿼리를 보면 다음과 같다.

select 
  account0_.account_id as account_1_0_, 
  account0_.email as email2_0_, 
  account0_.name as name3_0_, 
  account0_.password as password4_0_ 
from 
  account account0_ 
left outer join 
  orders orders1_ 
on 
  account0_.account_id=orders1_.account_id

left outer join 을 사용하며 on 도 자동으로 사용된다.

right join

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .rightJoin(account.orders, order)
  .fetch();

방식은 똑같다. rightJoin을 사용하면 된다. 마찬가지로 쿼리를 보자.

select 
  account0_.account_id as account_1_0_, 
  account0_.email as email2_0_, 
  account0_.name as name3_0_, 
  account0_.password as password4_0_ 
from 
  account account0_ 
right outer join 
  orders orders1_ 
on 
  account0_.account_id=orders1_.account_id

아주 간단하게 사용 가능하다.

fetch join

fetch join은 원래 sql의 join을 말하는 것은 아니고 jpa의 성능 향상을 위해 만들어진 것이다. n+1이 발생했을 때 사용하면 적절하다.
사용법은 아주 간단하다.

QOrder order = QOrder.order;
QAccount account = QAccount.account;
return from(order)
  .leftJoin(order.account, account).fetchJoin()
  .fetch();

이 조인은 fetch join이라는 것만 명시 해주면 된다. 쿼리를 보면 다음과 같다.

select 
  order0_.order_id as order_id1_3_0_, 
  account1_.account_id as account_1_0_1_, 
  order0_.account_id as account_3_3_0_, 
  order0_.order_date as order_da2_3_0_, 
  account1_.email as email2_0_1_, 
  account1_.name as name3_0_1_, 
  account1_.password as password4_0_1_ 
from 
  orders order0_ 
left outer join 
  account account1_ 
on 
  order0_.account_id=account1_.account_id

만약 fetch join을 사용하지 않고 해보자.

QOrder order = QOrder.order;
QAccount account = QAccount.account;
return from(order)
  .leftJoin(order.account, account)
  .fetch();

위와 같이 작성하면 쿼리는 다음과 같다.

select 
  order0_.order_id as order_id1_3_, 
  order0_.account_id as account_3_3_, 
  order0_.order_date as order_da2_3_ 
from 
  orders order0_ 
left outer join 
  account account1_ 
on 
  order0_.account_id=account1_.account_id


select 
  account0_.account_id as account_1_0_0_, 
  account0_.email as email2_0_0_, 
  account0_.name as name3_0_0_, 
  account0_.password as password4_0_0_ 
from 
  account account0_ 
where 
  account0_.account_id=?


select 
  account0_.account_id as account_1_0_0_, 
  account0_.email as email2_0_0_, 
  account0_.name as name3_0_0_, 
  account0_.password as password4_0_0_ 
from 
  account account0_ 
where 
  account0_.account_id=?


select 
  account0_.account_id as account_1_0_0_, 
  account0_.email as email2_0_0_, 
  account0_.name as name3_0_0_, 
  account0_.password as password4_0_0_ 
from 
  account account0_ 
where 
  account0_.account_id=?

이렇게 다음과 같이 한번 쿼리를 날렸지만 내부적으로는 n번의 쿼리를 더 날린셈이다. 이걸 보완하고자 fetchJoin이 만들어졌다.
또한 다음과 같은 코드가 있다고 하자.

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .join(account.orders, order)
  .fetch();


List<Account> accounts = accountRepository.findByInnerJoinOrders();
for(Account account :accounts){
  System.out.println(account.getOrders());
}

위의 봤듯이 join하는 쿼리이다. 하지만 Account 에서 orders를 가져올 경우 에러가 발생한다. 그 에러는 바로 LazyInitializationException 이다. 물론 fetch가 LAZY로 되어 있을 때 이야기다. 하지만 fetchJoin을 사용하면 이 방법을 해결 할 수 있다.

QAccount account = QAccount.account;
QOrder order = QOrder.order;
return from(account)
  .join(account.orders, order).fetchJoin()
  .fetch();

그때 그때 적절하게 사용해야 한다. 무조건 fetchJoin이 좋은 것은 아니다. 굳이 필요 없는 데이터를 가져올 수도 있기 때문이다. Dto를 만들어도 되고 fetchJoin을 써도 되고 강제로 초기화를 해도 되고 방법은 많다. 그때그때 맞게 잘 사용하자.

우리는 좀 더 세부적으로 join에 대해서 알아봤다. 아직 갈길이 먼듯하다.