Spring/QueryDSL

조인 - on 절

느리지만 꾸준하게 2022. 4. 20. 03:00

ON절을 활용해 조인(JPA 2.1 부터 지원)

  • 조인 대상 필터링
  • 연관관계 없는 엔티티 외부 조인 => 이게 많이 쓰인다.

조인 on절 test를 돌려보면

/**
 * 예) 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
 * // 회원이랑 팀을 조회하면서 조인을 하는데 팀의 이름이 teamA인 애만 가지고 온다.
 * JPQL: select m, t from Member m left join m.team t on t.name = 'teamA'
 */
@Test
public void join_on_filtering() {
    // Tuple가 나온 이유는 select절이 여러개이기 때문이다.
    List<Tuple> result = queryFactory
            .select(member, team)
            .from(member)
            .leftJoin(member.team, team).on(team.name.eq("teamA"))
            .fetch();
    for (Tuple tuple : result) {
        System.out.println("tuple = " + tuple);
    }
}

 

실행결과 아래와 같다.

/* select
        member1,
        team 
    from
        Member member1   
    left join
        member1.team as team with team.name = ?1 */
        
        
        // team의 이름까지 걸러내어 join이 된다.
        select
            member0_.id as id1_1_0_,
            team1_.id as id1_2_1_,
            member0_.age as age2_1_0_,
            member0_.team_id as team_id4_1_0_,
            member0_.username as username3_1_0_,
            team1_.name as name2_2_1_ 
        from
            member member0_ 
        left outer join
            team team1_ 
                on member0_.team_id=team1_.id 
                and (
                    team1_.name=?
                )










// teamB는 날라갔지만 left join이기 때문에 null로 나오게 된다.

tuple = [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple = [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]
tuple = [Member(id=5, username=member3, age=30), null]
tuple = [Member(id=6, username=member4, age=40), null]

 

그냥 join으로 바꾸면 어떻게 될까

@Test
public void join_on_filtering() {
    // Tuple가 나온 이유는 select절이 여러개이기 때문이다.
    List<Tuple> result = queryFactory
            .select(member, team)
            .from(member)
            .join(member.team, team).on(team.name.eq("teamA"))
            .fetch();
    for (Tuple tuple : result) {
        System.out.println("tuple = " + tuple);
    }
}

 

teamB는 같이 join하는 대상이 없기 때문에 빠져버리게 된다.

tuple = [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple = [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]

 

 

on절을 where절로 바꿀수가 있다.

 @Test
    public void join_on_filtering() {
        // Tuple가 나온 이유는 select절이 여러개이기 때문이다.
        List<Tuple> result = queryFactory
                .select(member, team)
                .from(member)
                .join(member.team, team)
//                .on(team.name.eq("teamA"))
                .where(team.name.eq("teamA"))
                .fetch();
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

 

위와 똑같이 결과 1 2만 나오게 된다.

tuple = [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple = [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]

 

 

leftJoin으로 해야 할 상황이 오면 where절을 쓰면 된다.

  • on절을 활용해 조인 대상을 필터링 할 때, 외부조인이 아니라 내부조인 inner join을 사용하면 where 절에서 필터링 하는 것과 동일
  • on 절을 활용한 조인 대상 필터링을 사용할 때, 내부조인이면 where 절로 해결 / 외부조인이 필요한 경우에만 이러한 기능 사용
@Test
    public void join_on_filtering() {
        // Tuple가 나온 이유는 select절이 여러개이기 때문이다.
        List<Tuple> result = queryFactory
                .select(member, team)
                .from(member)
                .join(member.team, team)
                // inner join이면 on 절로 걸러내나 where에 넣나 결과가 똑같다.
//                .on(team.name.eq("teamA"))

                // 만약 leftJoin(member.team, team)으로 해야 할 상황이 오면
                // where로 해서 풀면된다.
                .where(team.name.eq("teamA"))
                .fetch();
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

 

 

정리를 하자면

@Test
    public void join_on_filtering() {
        // Tuple가 나온 이유는 select절이 여러개이기 때문이다.
        List<Tuple> result = queryFactory
                .select(member, team)
                .from(member)
                .leftJoin(member.team, team)
                // on절로 join 하려는 대상을 줄여서 join을 할 수 있다.
                // 보통 left join인 경우에만 의미가 있다.
                // inner join이면 where절에서 결과가 걸러진다.
                // 즉 join 대상을 필터링 해서 가져와야 하는데 left join을 쓰는 경우에는 on 절을 활용하자.
                // team 의 데이터를 줄여서 그걸 가져온다. 만약 팀의 대상을 줄였는데 leftJoin이 아니고 innerjoin이다
                // 그러면 그냥 where 절에서 정리를 하는게 더 깔끔하다.
                .on(team.name.eq("teamA"))
//                .where(team.name.eq("teamA"))
                .fetch();
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

 

연관관계가 없는 엔티티를 외부 조인 해보자.

@Test
    public void join_on_no_relation() {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));
        em.persist(new Member("teamC"));

        List<Tuple> result = queryFactory
                .select(member, team)
                // from 해서 member를 join을 하는데
                // 보통은 member.team 이렇게 한다.
                // 여기서는 leftJoin(team) 썼다. => id를 매칭 안해서 (member.username.eq(team.name)) 이름으로만 join을 한다.
                .from(member)
                //.leftJoin(member.team, team)
                // member team 막 조인을 한다 이런 경우에는 on을 이용
                // on절을 필터를 줄이는 역할
                .leftJoin(team).on(member.username.eq(team.name))
                .fetch();

        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

 

member의 이름과 team의 이름이 같은 경우에는 오른쪽에 있는 조인 대상을 실행결과만큼만 가져오게 된다.

on(member.username.eq(team.name))

 

실행결과는 아래와 같다.

tuple = [Member(id=3, username=member1, age=10), null]
tuple = [Member(id=4, username=member2, age=20), null]
tuple = [Member(id=5, username=member3, age=30), null]
tuple = [Member(id=6, username=member4, age=40), null]
tuple = [Member(id=7, username=teamA, age=0), Team(id=1, name=teamA)]
tuple = [Member(id=8, username=teamB, age=0), Team(id=2, name=teamB)]

// 오른쪽 대상을 필터링해서 없기 때문에 
// on(member.username.eq(team.name)) 조건을 만족하지 않는다.
tuple = [Member(id=9, username=teamC, age=0), null]

 

그냥 join을 하게 되면 null이 다 없어지게 된다.

@Test
    public void join_on_no_relation() {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));
        em.persist(new Member("teamC"));

        List<Tuple> result = queryFactory
                .select(member, team)
                // from 해서 member를 join을 하는데
                // 보통은 member.team 이렇게 한다.
                // 여기서는 leftJoin(team) 썼다. => id를 매칭 안해서 (member.username.eq(team.name)) 이름으로만 join을 한다.
                .from(member)
                // member team 막 조인을 한다 이런 경우에는 on을 이용
                // on절을 필터를 줄이는 역할
                .join(team).on(member.username.eq(team.name))
                .fetch();

        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }
/* select
        member1,
        team 
    from
        Member member1   
    inner join
        Team team with member1.username = team.name */

// on절에서 usernam이랑 team이름이 같다 이거만 있다.

select
            member0_.id as id1_1_0_,
            team1_.id as id1_2_1_,
            member0_.age as age2_1_0_,
            member0_.team_id as team_id4_1_0_,
            member0_.username as username3_1_0_,
            team1_.name as name2_2_1_ 
        from
            member member0_ 
        inner join
            team team1_ 
                on (
                    member0_.username=team1_.name
                )

 

 

즉 member.team 이 있으면 id가 매칭된것이 들어가게 되고

.leftJoin(member.team, team).on(member.username.eq(team.name))

 

없으면 member0_.username=team1_.name 이거만 필터링 하게 된다.

 

.leftJoin(team).on(member.username.eq(team.name))

 

  • 하이버네이트 5.1부터 `on`을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능이 추가 / 내부 조인도 가능
  • leftJoin() 부분에 일반 조인과 다르게 엔티티 하나만 들어감.

 

  • 일반조인  ->  leftJoin(member.team, team)
  • on조인 ->from(member).leftJoin(team).on(xxx)

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 실전! Querydsl >

https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard

 

실전! Querydsl - 인프런 | 강의

Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...

www.inflearn.com

 

'Spring > QueryDSL' 카테고리의 다른 글

서브 쿼리  (0) 2022.04.20
조인 - 페치 조인  (0) 2022.04.20
조인 - 기본 조인  (0) 2022.04.20
집합  (0) 2022.04.20
정렬 & 페이징  (0) 2022.04.20