JOIN
이란 여러 테이블의 레코드를 조합하여 하나의 열로 표현한 것이다.
서로 관계가 있는 데이터가 여러 테이블로 나뉘어 저장되므로 각 테이블에 저장된 데이터를 효과적으로 검색하기 위해 필요하다.
JPQL
, Querydsl
은 조인연산을 제공하여 복잡한 쿼리 작성을 수행할 수 있다.
조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고, 두 번째 파라미터에 alias로 사용할 QType
을 지정하면 된다
List<Member> result = queryFactory
.selectFrom(member)
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
팀 A에 소속된 모든 회원을 출력하는 쿼리이다.
실제로 작동되는 쿼리는 다음과 같다.
select
member0_.user_id as user_id1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.team_id
where
team1_.name=?
SQL에서 사용하는 innerjoin, left outer join, right outer join 모두 사용이 가능하다.
세타 조인
은 연관관계가 없는 필드로 조인하는 것을 말한다.
List<Member> result = queryFactory
.select(member)
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
회원의 이름과 팀 이름은 연관관계가 없지만 두 필드의 값이 같은 회원을 조회한다면 세타 조인을 통해 쿼리를 작성할 수 있다.
실제로 작동되는 쿼리는 다음과 같다.
select
member0_.user_id as user_id1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_ cross
join
team team1_
where
member0_.username=team1_.name
cross join
이란 집합에서 나올 수 있는 모든 경우의 수이기 때문에 where 절을 통해 필터링하는 것을 알 수 있다.
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(member.team, team).on(team.name.eq("teamA"))
.fetch();
회원을 모두 조회하지만 팀 이름이 'teamA'인 경우 Team 조회를 수행한다.
팀 이름이 'teamA'인 경우 Team 정보를 같이 조회하지만 아닌 경우 null 값이 들어간다.
select
member0_.user_id as user_id1_0_0_,
team1_.team_id as team_id1_1_1_,
member0_.age as age2_0_0_,
member0_.team_id as team_id4_0_0_,
member0_.username as username3_0_0_,
team1_.name as name2_1_1_
from
member member0_
left outer join
team team1_
on member0_.team_id=team1_.team_id
and (team1_.name=?)
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(team).on(member.username.eq(team.name))
.fetch();
연관관계가 없는 엔티티 외부 조인은 left join id 값으로 매칭을 해주는 기존 연관관계와 다르게 순수 on 절의 조건으로 매칭을 시킨다.
일반 조인 : leftjoin(member.team, team)
on 조인 : join(team).on(member.username.eq(team.name))
fetch join은 sql 조인을 활용하여 연관된 엔티티를 sql 한번에 조회하는 기능으로 성능 최적화에 사용된다.
Member result = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
엔티티 설계를 할 때 N+1문제로 인해 LAZY 설정을 수행했지만 fetch join을 통해 연관된 엔티티까지 한 번에 조회가 가능하다.
select
member0_.user_id as user_id1_0_0_,
team1_.team_id as team_id1_1_1_,
member0_.age as age2_0_0_,
member0_.team_id as team_id4_0_0_,
member0_.username as username3_0_0_,
team1_.name as name2_1_1_
from
member member0_
inner join
team team1_
on member0_.team_id=team1_.team_id
where
member0_.username=?