본문 바로가기

Spring/Querydsl

Querydsl- 조인

@Test
public void join() throws Exception{
    List<Member> result = queryFactory
            .selectFrom(member)
            .join(member.team, team)
            .where(team.name.eq("teamA"))
            .fetch();

    for (Member m : result) {
        assertThat(m.getTeam().getName()).isEqualTo("teamA");
    }
}

/**
 * 세타 조인
 * 회원의 이름이 팀 이름과 같은 회원 조회
 */
@Test
public void theta_join() throws Exception{
    // 세타조인 예제를 위한 엔티티
    em.persist(new Member("teamA"));
    em.persist(new Member("teamB"));

    // 세타조인 = 조인 + where
    List<Member> result = queryFactory
            .select(member)
            .from(member, team)  //모든 member와 team을 모두 조인 (연관관계가 없더라도 전부 다 조인)
            .where(member.username.eq(team.name))  // 조인된 엔티티들중 member.username == team.name인 엔티티만 조회
            .fetch();

    assertThat(result.get(0).getUsername()).isEqualTo("teamA");
    assertThat(result.get(1).getUsername()).isEqualTo("teamB");

}

 세타 조인 예제에서 보이듯이 from()에 member와 team을 넣으면 연관관계가 없는 경우에도 관계없이 모든 member와  team이 조인된다(막 조인). 따라서 테이블이 뻥튀기 될 수 있다. 만일 member가 4개 team이 3개인 경우 from(member, team)을 하게 되면 총 12개의 member가 조회될 것이다.

 

from()으로 인한 데이터 뻥튀기 예제

 - 이미 member1,2,3,4와 teamA,B,C가 저장되어 있다.

@Test
@Rollback(value = false)
public void theta_join2() throws Exception{
    // from()으로 인한 데이터 뻥튀기
    List<Member> result = queryFactory
            .select(member)
            .from(member, team)  //모든 member와 team을 모두 조인 (연관관계가 없더라도 전부 다 조인)
            .fetch();

    for (Member m : result) {
        System.out.println("m = " + m + " m.team= "+m.getTeam());
    }
}

출력>>>
m = Member(id=3, username=member1, age=10) m.team= Team(id=1, name=teamA)
m = Member(id=3, username=member1, age=10) m.team= Team(id=1, name=teamA)
m = Member(id=3, username=member1, age=10) m.team= Team(id=1, name=teamA)
m = Member(id=4, username=member2, age=20) m.team= Team(id=1, name=teamA)
m = Member(id=4, username=member2, age=20) m.team= Team(id=1, name=teamA)
m = Member(id=4, username=member2, age=20) m.team= Team(id=1, name=teamA)
m = Member(id=5, username=member3, age=30) m.team= Team(id=2, name=teamB)
m = Member(id=5, username=member3, age=30) m.team= Team(id=2, name=teamB)
m = Member(id=5, username=member3, age=30) m.team= Team(id=2, name=teamB)
m = Member(id=6, username=member4, age=40) m.team= Team(id=2, name=teamB)
m = Member(id=6, username=member4, age=40) m.team= Team(id=2, name=teamB)
m = Member(id=6, username=member4, age=40) m.team= Team(id=2, name=teamB)

4(member) x 3(team) = 12번 조회된다. 쿼리가 12번 나간다는 말은 아니고 조인된 테이블의 행 수가 12개가 된다는 말이다.

조인된 테이블 결과

On

하이버네이트 5.1부터 on 을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능이 추가되었다. 물론 내부 조인도 가능하다.

주의! 문법을 잘 봐야 한다. leftJoin() 부분에 일반 조인과 다르게 엔티티 하나만 들어간다.

  • 일반조인: leftJoin(member.team, team) ( -> fk와 pk로 조인)
  • on조인: from(member).leftJoin(team).on(xxx) ( -> 막 조인 후 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("t=" + tuple);
	} 
}

페치 조인

 페치 조인을 하기위해서는 join(), leftJoin()과 같은 조인 메서드 뒤에 fetchJoin() 메서드를 붙여준다. 페치 조인을 하게 되면 연관된 엔티티를 모두 즉시로딩으로 한번에 조회해 온다. 

Member findMember
            = queryFactory
            .selectFrom(member)
            .join(member.team, team). fetchJoin()  // 페치 조인 적용. member의 엔티티 다 끌고옴.
            .where(member.username.eq("member1"))
            .fetchOne();

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

Querydsl- 동적쿼리  (0) 2023.08.05
Querydsl- 프로젝션  (0) 2023.08.04
Querydsl- 집합  (0) 2023.07.26
Querydsl- 정렬과 페이징  (0) 2023.07.25
Querydsl- Q 인스턴스  (0) 2023.07.22