QueryDSL - 기본문법(집합, 조인)

2025. 8. 23. 11:34·Back-End/queryDSL

집합 함수

    @Test
    public void aggregation() {
        List<Tuple> result = queryFactory
                .select(
                        member.count(),
                        member.age.sum(),
                        member.age.avg(),
                        member.age.max(),
                        member.age.min()
                )
                .from(member)
                .fetch();

        Tuple tuple = result.get(0);
        assertThat(tuple.get(member.count())).isEqualTo(4);
        assertThat(tuple.get(member.age.sum())).isEqualTo(100);
        assertThat(tuple.get(member.age.avg())).isEqualTo(25);
        assertThat(tuple.get(member.age.max())).isEqualTo(40);
        assertThat(tuple.get(member.age.min())).isEqualTo(10);
    }
  • count(), sum(), avg(), max(), min() 함수 제공
  • 결과는 Tuple로 반환된다. → 추후에 설명

GroupBy

    @Test
    public void group() throws Exception {
        List<Tuple> result = queryFactory
                .select(team.name, member.age.avg())
                .from(member)
                .join(member.team, team)
                .groupBy(team.name)
                .fetch();

        Tuple teamA = result.get(0);
        Tuple teamB = result.get(1);

        assertThat(teamA.get(team.name)).isEqualTo("teamA");
        assertThat(teamA.get(member.age.avg())).isEqualTo(15); //(10+20) / 2

        assertThat(teamB.get(team.name)).isEqualTo("teamB");
        assertThat(teamB.get(member.age.avg())).isEqualTo(35); //(30 + 40) / 2
    }

위에서 having() 절도 추가 가능하다

조인

/*
    * 팀 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(), innterJoin() : 내부 조인(innerJoin) 실행
  • leftJoin() : left 외부 조인(left outer join)
  • rightJoin() : right 외부 조인(right outer join)

세타 조인

@Test
    public void theta_join() throws Exception {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));

        List<Member> result = queryFactory
                .select(member)
                .from(member, team) // 조인을 쓰지 않고 그냥 from에서 가져와서 붙여버린다.
                .where(member.username.eq(team.name))
                .fetch();

        assertThat(result)
                .extracting("username")
                .containsExactly("teamA", "teamB");
    }

  • from 절에서 여러 엔티티를 넣어서 세타 조인한다.
  • 외부 조인은 불가능 → 다음에 설명할 조인 on을 사용하면 외부 조인 가능

조인 - on 절

ON절을 활용한 조인을 사용하는 경우

  1. 조인 대상 필터링
  2. 연관관계 없는 엔티티 외부 조인

1. 조인 대상 필터링

  • 회원과 팀을 조인하면서 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
/*
    * 예) 회원과 팀을 조인하면서 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회
    * JPQL: select m, t from Member m left join m.team t on 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);
        }

    }

  • member 기준으로 leftJoin을 실행 하고, teamA인것만 조인을 시켰기 때문에, teamB인 애들은 조인이 되지않아 Team이 null로 출력되었다.

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

연관관계가 아닌, 필드의 값으로 조인을 하는 기능

/*
     * 연관관계가 없는 엔티티 외부 조인
     * 회원의 이름이 팀 이름과 같은 대상 외부 조인
     * */
    @Test
    public void join_on_no_relation() throws Exception {
        em.persist(new Member("teamA"));
        em.persist(new Member("teamB"));
        em.persist(new Member("teamC"));

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

leftJoin() 부분에 일반 조인과 다르게 엔티티 하나만 들어간다.

  • 일반 조인: leftJoin(member.team, team) → id값으로 조인 시킨다.
  • on조인: from(member).leftjoin(team).on(xxx) → on절에 선언한 필드의 값으로만 조인시킨다.

페치 조인

페치 조인 미적용

지연로딩으로 Member, Team SQL 쿼리가 각각 실행된다.

    @Test
    public void fetchJoinNo() {
        em.flush();
        em.clear(); // 영속성 컨텍스트 초기화

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

        // 영속성 커넥스트에 loading되었는지 확인
        // member에 LazyLoading된 team은 loading되지 않아 false값이 기대된다.
        boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
        assertThat(loaded).as("페치 조인 미적용").isFalse();
    }

→ member만 조회하는 SQL이 실행되어, team은 영속성 컨텍스트에 영속되지 않았다. → Team은 프록시 객체로만 조회됨

페치 조인 적용

즉시로딩으로 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();

        // 영속성 컨텍스트에 team이 loading되었는지 확인
        // fetch조인을 통해서 연관된 엔티티를 가져온다. -> ture값이 기대됨.
        boolean loaded = emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam());
        assertThat(loaded).as("페치 조인 적용").isTrue();
    }

→ 즉시 로딩되어, 연관된 Team의 모든 필드를 같이 조인하여, 영속성 컨텍스트에 로딩 시킨다.

 

요약

- QueryDSL 에서의 기본 집합함수 제공기능과, GroupBy, Having기능을 확인

- JPA, JPQL에서 사용했던, 조인 절의 활용을 확인 할 수 있엇다.

- FetchJoin 관련하여 다시 한번 살펴 보았는데, LazyLoading 전략을 이용하면서 엔티티의 정규화 및 연관관계 설정을 하고, 그로인해 발생하는 JPA의 N+1문제를 페치 조인을 통해서 해결 할수 있다는 사실을 다시 한번 복습하였다.

 

참고

해당 글은 김영한님의 실전! Query DSL을 공부하며 작성한 글입니다.

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

 

실전! Querydsl| 김영한 - 인프런 강의

현재 평점 5.0점 수강생 15,897명인 강의를 만나보세요. Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요! Querydsl을 기초부터 실무활용까지 한번에 배울 수 있습니다., 단순한 기능 설명을

www.inflearn.com

반응형
저작자표시 (새창열림)
'Back-End/queryDSL' 카테고리의 다른 글
  • QueryDSL - 중급문법(프로젝션과 결과 반환 기본과 DTO, @QueryProjection)
  • QueryDSL - 기본문법(서브쿼리, Case, 상수와 문자 더하기)
  • QueryDSL - 기본 문법(검색, 결과, 정렬, 페이징)
  • JPQL 과 QueryDSL 문법 비교 및 Q-Type활용
LightSource
LightSource
어제보단 발전한 오늘의 나를 위한 블로그
    반응형
  • LightSource
    LightSourceCoder
    LightSource
  • 전체
    오늘
    어제
    • 분류 전체보기 (169)
      • Git (4)
      • Language (6)
        • Java (6)
      • Back-End (75)
        • Spring Boot (4)
        • MyBatis (1)
        • Oracle (1)
        • PL SQL (3)
        • JPA (25)
        • Spring Data JPA (5)
        • Spring MVC (8)
        • Spring (15)
        • Spring Security (2)
        • Redis (1)
        • queryDSL (10)
      • Front-End (38)
        • 아이오닉 (2)
        • JSP (7)
        • JavaScript (4)
        • React (16)
        • TypeScript (3)
        • Angular (6)
      • AWS (1)
      • CI & CD (2)
      • 개발지식 (14)
        • 네트워크 (9)
        • CS 지식 (4)
      • 데이터모델링 (2)
      • Tool (1)
      • 프로젝트 (5)
      • 독후감 (2)
      • 잡생각 (0)
      • 면접 준비 (1)
      • 알고리즘 (14)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배열요소삭제
    배요소열추가
    react
    배열요소수정
    리액트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
LightSource
QueryDSL - 기본문법(집합, 조인)
상단으로

티스토리툴바