기본조인

조인의 종류는 join(내부 조인), innerJoin(내부 조인), leftJoin(left 외부 조인), rightJoin(right 외부 조인)

조인의 기본 문법은 첫 번째 파라미터에 조인 대상을 지정하고

두 번째 파라미터에 별칭으로 사용할 Q 타입을 지정하면 된다

List<Member> result = queryFactory
        .selectFrom(member)
        .leftJoin(member.team, team)
        .where(team.name.eq("teamA"))
        .fetch();

 

세타조인

세타 조인은 연관관계가 없는 필드로 조인할 경우 사용한다

from 절에서 여러 엔티티를 선택한다

외부 조인이 불가능하지만, 조인 on을 사용하면 외부 조인이 가능하다

List<Member> result = queryFactory
        .select(member)
        .from(member, team)
        .where(member.username.eq(team.name))
        .fetch();

 

조인 - on절

on절은 다음과 같은 기능을 한다

1) 조인 대상 필터링

2) 연관관계 없는 엔티티 외부 조인(세타 조인)

 

만약 on절과 내부 조인을 사용할 경우 where 절에서 필터링 하는 것과 기능이 똑같다. 따라서 외부 조인일 때에만 on절을 활용하는 것이 옳바른 방법이다

 

조인 대상 필터링

//JQPL
select m, t from Member m left join m.team t on t.name = 'teamA'

//SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='teamA'

//Querydsl
List<Tuple> result = queryFactory
        .select(member, team)
        .from(member)
        .leftJoin(member.team, team)
        .on(team.name.eq("teamA"))
        .fetch();

 

연관관계 없는 엔티티 외부 조인(세타 조인)

//JPQL
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name

//SQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name

//Querydsl
List<Tuple> result1 = queryFactory
        .select(member, team)
        .from(member)
        .leftJoin(team)
        .on(member.username.eq(team.name))
        .fetch();

 

조인 - 패치조인

페치 조인은 SQL에서 제공하는 기능은 아니다

SQL 조인을 활용해 지연로딩으로 설정된 엔티티를 한 번에 조회하는 기능이다

주로 성능 최적화에 사용하는 방법이다

 

JPA의 성능을 최적화하기 위해 대부분의 연관관계는 지연 로딩(LAZY)으로 설정하는 것이 좋다

만약 즉시 로딩(EAGER)로 설정하면 해당 엔티티를 조인하는 다른 쿼리에서

예상하지 못한 조인이 발생할 수 있기 때문이다

결론은 대부분의 연관관계는 지연 로딩(LAZY)으로 설정하고, 필요한 경우 페치 조인을 사용해 조회하자

 

Member 엔티티

Member 엔티티의 team이 지연 로딩으로 설정된 것을 볼 수 있다

따라서 페치 조인을 적용하지 않고 일반 조인으로 Member를 조회하면 Team은 조회되지 않는다

public class Member {
    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}

 

패치조인 미적용

Member findMember = queryFactory
        .selectFrom(member)
        .where(member.username.eq("member1"))
        .fetchOne();

 

패치조인 적용

Member findMember = queryFactory
        .selectFrom(member)
        .join(member.team, team).fetchJoin()
        .where(member.username.eq("member1"))
        .fetchOne();

 

Case문

select, where, order by에서 사용 가능하다

 

단순한 조건

List<String> result = queryFactory
        .select(member.age
                .when(10).then("열살")
                .when(20).then("스무살")
                .otherwise("기타")
        )
        .from(member)
        .fetch();

 

복잡한 조건

caseBuilder를 사용

List<String> result = queryFactory
        .select(new CaseBuilder()
                .when(member.age.between(0, 20)).then("0~20")
                .when(member.age.between(21, 30)).then("21~30")
                .otherwise("기타")
        )
        .from(member)
        .fetch();

 

orderBy에서 Case 문 함께 사용하기

쿼리 조건은 다음과 같다.

1) 0~30살이 아닌 회원을 가장 먼저 출력

2) 0~20살 회원 출력

3) 21~30살 회원 출력

NumberExpression<Integer> rank = new CaseBuilder()
        .when(member.age.between(0, 20)).then(2)
        .when(member.age.between(21, 40)).then(3)
        .otherwise(1);

List<Tuple> result = queryFactory
        .select(member.username, rank)
        .from(member)
        .orderBy(rank.desc())
        .fetch();

 

상수 추가하기

List<Tuple> result = queryFactory
        .select(member.username, Expressions.constant("A"))
        .from(member)
        .fetch();

 

문자 더하기

concat를 사용

List<String> result = queryFactory
        .select(member.username.concat("_").concat(member.age.stringValue()))
        .from(member)
        .where(member.username.eq("member1"))
        .fetch();