흰 스타렉스에서 내가 내리지

[Querydsl] 조인 본문

JPA

[Querydsl] 조인

주씨. 2024. 4. 18. 05:17
728x90
join(조인 대상, 별칭으로 사용할 Q타입)

# 기본 조인

/**
 * 팀 A에 소속된 모든 회원
 */
@Test
public void join(){
    QMember member = QMember.member;
    QTeam team = QTeam.team;

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

    assertThat(result)
            .extracting("username")
            .containsExactly("member1", "member2");
}

 

  • join(), innerJoin() : 내부 조인 (inner join)
  • leftJoin() : left 외부 조인 (left outer join)
  • rightJoin() : right 외부 조인 (right outer join)
  • JPQL 의 on 과 성능 최적화를 위한 fetch 조인 제공 → 나중에

 

# 세타 조인 - 연관관계가 없는 필드끼리 조인

/**
 * 세타 조인 (연관관계가 없는 필드로 조인)
 * 회원의 이름이 팀 이름과 같은 회원 조회
 */
@Test
public void theta_join(){
    em.persist(new Member("teamA"));
    em.persist(new Member("teamB"));

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

    assertThat(result)
            .extracting("username")
            .containsExactly("teamA", "teamB");
}
  • from 절에 여러 엔티티를 선택해서 세타 조인
  • 외부 조인 불가능 → 다음에 설명할 조인 on 을 사용하면 외부 조인 가능

 

# 조인 - on 절

 

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);
    }

}

 

결과

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]

 

 

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(){
    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);
    }

}

 

  • Hibernate 5.1 부터 on 을 사용해서 서로 관게가 없는 필드로 외부 조인하는 기능이 추가되었다. 
  • leftJoin() 부분에 일반 조인과 다르게 엔티티 하나만 들어간다
    • 일반조인 : leftJoin(member.team, team)
    • on조인 : from(member).leftJoin(team).on(xxx)

 

결과

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)]
t = [Member(id=6, username=teamB, age=0), Team(id=2, name=teamB)]

 

 

# 페치 조인

@PersistenceUnit
EntityManagerFactory emf;

/**
 * 페치 조인 적용
 * 즉시로딩으로 Member, Team SQL 쿼리 조인으로 한 번에 조회
 */
@Test
public void fetchJoinUse(){
    em.flush();
    em.clear();

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

    boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
    assertThat(loaded).as("페치 조인 미적용").isTrue();

}

 

 

  • join(), leftJoin() 등 조인 기능 뒤에 fetchJoin() 이라고 추가하면 된다. 

'JPA' 카테고리의 다른 글

[Querydsl] 프로젝션 결과 반환 - DTO 조회  (0) 2024.04.19
[Querydsl] 서브쿼리  (0) 2024.04.19
[Querydsl] 집합 함수, GroupBy  (0) 2024.04.18
[Querydsl] 정렬과 페이징  (0) 2024.04.18
[Querydsl] 결과 조회  (0) 2024.04.18