본문 바로가기
JPA

[QueryDSL] 동적 쿼리 게시물 조회 기능 구현 (기본문법)

by 그리득 2024. 6. 16.
728x90

요즘 회사 과제로 필터가 적용된 조회 기능을 구현하고 있다.

이를 위해 동적 쿼리를 사용한 게시물 조회 기능을 만들고자 QueryDSL을 사용해보려고 한다.

개인공부를 위해 작성한 글로 이 글에서는 JPQL과 QueryDSL을 비교하며 기본적인 문법부터 알아보겠습니다.

 

JPQL과 QueryDSL 비교

먼저, JPQL을 사용하여 username으로 데이터를 조회하는 예제를 보겠습니다.

@Test
void startJPQL() {
    Member findMember =
            em.createQuery("select m from Member m where m.username = :username", Member.class)
            .setParameter("username", "member1")
            .getSingleResult();
    Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

 

이때 실행된 JPQL 쿼리는 다음과 같습니다:

    select
        member0_.member_id as member_i1_1_,
        member0_.age as age2_1_,
        member0_.team_id as team_id4_1_,
        member0_.username as username3_1_ 
    from
        member member0_ 
    where
        member0_.username=?

 

이번에는 QueryDSL을 사용하여 username으로 데이터를 조회하는 예제를 보겠습니다.

@Test
void startQuerydsl() {
    JPAQueryFactory queryFactory = new JPAQueryFactory(em);
    QMember m = new QMember("m");
    Member findMember = queryFactory
                            .select(m)
                            .from(m)
                            .where(m.username.eq("member1"))//파라미터 바인딩 처리
                            .fetchOne();
    Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

 EntityManager로 JPAQueryFactory생성

 Querydsl은 JPQL빌더

 JPQL : 문자(실행 시점 오류), Querydsl : 코드 (컴파일 시점 오류)

 JPQL : 파라미터 바인딩 직접, Querydsl : 파라미터 바인딩 자동 처리

 

날아간 쿼리는 위의 JPQL에서와 같습니다.

    select
        member0_.member_id as member_i1_1_,
        member0_.age as age2_1_,
        member0_.team_id as team_id4_1_,
        member0_.username as username3_1_ 
    from
        member member0_ 
    where
        member0_.username=?

 

 


Q타입 인스턴스 사용 방법

 

QueryDSL에서 Q타입 인스턴스를 사용하는 방법은 세 가지가 있습니다.

 

1. new를 사용하는 방법

QMember m = new QMember("m");

Member findMember = queryFactory
        .select(m)
        .from(m)
        .where(m.username.eq("member1"))
        .fetchOne();

제일 권장하지 않는 방법이다.

 

 

2. static 필드 사용하기

QMember m = QMember.member;

Member findMember = queryFactory
        .select(m)
        .from(m)
        .where(m.username.eq("member1"))
        .fetchOne();

 

 

3. static 필드를 import하여 사용하는 방법(가장 권장되는 방법)

import static study.querydsl.entity.QMember.member;

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

 


검색 조건 쿼리

 

검색 조건이 2개 이상일 때 and를 사용하여 조건을 추가할 수 있습니다.

@Test
void search1() {
    Member findMember = queryFactory
            .selectFrom(member)//요렇게 select + from 합치기 가능
            .where(member.username.eq("member1")
                    .and(member.age.eq(10)))//and 또는 or로 체인걸기
            .fetchOne();

    Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

• 검색 조건은 .and(), .or() 를 메서드 체인으로 연결할 수 있다.

참고 : select 와 from을 위의 search1처럼 합칠 수 있음

 

Querydsl은 JPQL이 제공하는 모든 검색조건을 제공한다.

member.username.eq("member1") // username = 'member1'
member.username.ne("member1") // username != 'member1'
member.username.eq("member1").not() // username != 'member1'
member.username.isNotNull() // 이름이 is not null
member.age.in(10, 20) // age in (10,20)
member.age.notIn(10, 20) // age not in (10, 20)
member.age.between(10,30) // between 10, 30
member.age.goe(30) // age >= 30
member.age.gt(30) // age > 30
member.age.loe(30) // age <= 30
member.age.lt(30) // age < 30
member.username.like("member%") // like 검색
member.username.contains("member") // like ‘%member%’ 검색
member.username.startsWith("member") // like ‘member%’ 검색 ...

 

또한 아래와 같이 and조건을 파라미터로 처리할 수 있다.

@Test
void searchAndParam() {
    Member findMember = queryFactory
            .selectFrom(member)
            .where(
            		//and조건 2개 파라미터
                    member.username.eq("member1"), member.age.eq(10) 
            )
            .fetchOne();

    Assertions.assertThat(findMember.getUsername()).isEqualTo("member1");
}

• where에 파라미터로 검색조건을 추가하면 AND조건이 추가된다.

• 이 경우 null값은 무시 -> 메서드 추출을 총해서 동적쿼리를 깔끔하게 만들 수 있다.

 


결과 조회

 

QueryDSL에서는 다양한 결과 조회 메서드를 제공합니다.

  • fetch(): 리스트 조회, 데이터가 없으면 빈 리스트 반환
  • fetchOne(): 단건 조회, 결과가 없으면 null, 둘 이상이면 NonUniqueResultException
  • fetchFirst(): 첫 번째 결과 조회, limit(1).fetchOne()과 동일

예제를 통해 사용해보겠습니다.

@Test
void resultFetch() {
    // 다수 조회
    List<Member> fetch = queryFactory.selectFrom(member).fetch();

    // 단건 조회
    Member fetchOne = queryFactory.selectFrom(member)
                                  .where(member.username.eq("member1"))
                                  .fetchOne();

    // 처음 한 건 조회
    Member fetchFirst = queryFactory.selectFrom(member).fetchFirst();
}

 

수행된 쿼리는 다음과 같습니다:

// 리스트 조회
select
    member0_.member_id as member_i1_1_,
    member0_.age as age2_1_,
    member0_.team_id as team_id4_1_,
    member0_.username as username3_1_ 
from
    member member0_

// 단건 조회
select
    member0_.member_id as member_i1_1_,
    member0_.age as age2_1_,
    member0_.team_id as team_id4_1_,
    member0_.username as username3_1_ 
from
    member member0_ 
where
    member0_.username=?

// 처음 한 건 조회
select
    member0_.member_id as member_i1_1_,
    member0_.age as age2_1_,
    member0_.team_id as team_id4_1_,
    member0_.username as username3_1_ 
from
    member member0_ 
limit ?

 

 

•참고 문헌 : 자바 ORM 표준 JPA 프로그래밍 / 김영한