ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Querydsl - 조인
    JPA/Querydsl 2023. 8. 13. 00:03

    조인 - 기본 조인

     

    첫 번째 파라미터에 조인 대상을 지정, 두 번째 파라미터에 별칭으로 사용할 Q타입을 지정하면 된다.

    /**
    * 팀 A에 소속된 모든 회원
    */
    @Test
    public void join() {
        List<Member> result = queryFactory
                .selectFrom(member)
                .join(member.team, team)
                .where(team.name.eq("teamA"))
                .fetch();
    
        assertThat(result)             
                .extracting("username")
                .containsExactly("member1", "member2");
    }

    join(member.team, team)이라 되어있는데 뒤에 있는 team은 QTeam.team인데 static import로 인해서 team으로 나오는 것이다.

     

     

    연관관계가 없는 엔티티의 조인 (세타 조인)

    @Test
    public void theta_join() {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));
    
        List<Member> result = queryFactory
                .select(member)
                .from(member, team) // 연관관계가 없는 join
                .where(member.username.eq(team.name))
                .fetch();
    
        assertThat(result)
                .extracting("username")
                .containsExactly("teamA", "teamB");
    }

    from 절에 연관관계가 없는 두 엔티티를 입력하여 member와 team을 전부 select 한 다음에 그 뒤에 where 절의 조건으로 fetch()라는 결과를 반환한다.

     

     

     

     

     

    조인 - on절 포함

    1. 조인 대상을 필터링

    2. 연관관계 없는 엔티티의 외부 조인

     

     

     

    1. 조인 대상 필터링

    /**
    * 회원과 팀을 조회할 때 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
    * * JPQL: 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'
    */
    @Test
    public void join_on_filtering() {
        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);
        }
    }

    leftJoin을 했고 on절의 조건에 team의 이름이 teamA인 것이라고 조건을 걸었다. member는 다 가져오고, team은 teamA일 때만 데이터를 가져오는 것이다.

     

     

    콘솔 결과창

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

    만약 leftjoin이 아니라 join이라면 teamB인 member3과 member4는 빠질 것이다.

     

    on 절을 활용해 필터링을 진행할 때, 외부조인이 아니라 내부조인을 사용하면 where 절에서 필터링 하는 것과 기능이 동일하다. 따라서 내부조인이면 익숙하게 사용하는 where 절로 해결하고 외부 조인이 필요할 때만 on절을 사용하는 것이 낫다.

     

     

     

    2. 연관관계 없는 엔티티 외부 조인

    /**
    * * 회원의 이름과 팀의 이름이 같은 대상 외부 조인
    * * 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
    */
    @Test
    public void join_on_no_relation() throws Exception {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));
            
        List<Tuple> result = queryFactory
                .select(member, team)
                .from(member)
                .leftJoin(team).on(member.username.eq(team.name))
                .fetch();
            
        for (Tuple tuple : result) {
            System.out.println("tuple = " + tuple);
        }
    }

    원래 join을 할 때에는 join(member.team, team)등으로 했지만 세타 조인을 할 때에는 매칭하지 않고 on 절에 있는 조건들로만 필터링이 된다.

     

     

    콘솔 결과 창

    t=[Member(id=1, username=member1, age=10), null]
    t=[Member(id=2, username=member2, age=20), null]
    t=[Member(id=3, username=member3, age=30), null]
    t=[Member(id=4, username=member4, age=40), null]
    t=[Member(id=5, username=teamA, age=0), Team(id=1, name=teamA)] // member와 team의 이름이 같을 때만 조회
    t=[Member(id=6, username=teamB, age=0), Team(id=2, name=teamB)] // member와 team의 이름이 같을 때만 조회

     

     

     

     

    조인 - 페치 조인

     

    페치 조인은 연관된 엔티티를 SQL 한번에 조회해서 성능 최적화를 사용하는 기능이다.

    @Test
    public void fetchJoinUse() {
        em.flush();
        em.clear();
    
        Member findMember = queryFactory
                .selectFrom(member)
                .join(member.team, team).fetchJoin()
                .where(QMember.member.username.eq("member1"))
                .fetchOne();
    
        boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
        assertThat(loaded).as("페치 조인 미적용").isTrue();
    }

    join문 뒤에 fetchJoin()만 넣어주면 된다. 이렇게 하면 member와 team을 한 번에 조회해서 쿼리 최적화를 할 때 유용해진다.

    'JPA > Querydsl' 카테고리의 다른 글

    Querydsl - Case 문  (0) 2023.08.13
    Querydsl - 서브 쿼리  (0) 2023.08.13
    Querydsl - 정렬과 페이징, 집합  (0) 2023.08.12
    Querydsl - 검색(select)과 결과 조회  (0) 2023.08.12
    Querydls과 JPQL의 차이  (0) 2023.08.12
Designed by Tistory.