[Spring] QueryDSL 세팅부터 사용법을 알아보자!1

신창호·2024년 2월 5일
0

Spring

목록 보기
5/9
post-thumbnail

여차여차하다보니, 드뎌 QueryDSL를 직접 다뤄볼 수 있는 기회까지 오게되었다.

QueryDSL은 Java로 SQL과 같은 쿼리를 타입-세이프하게 작성할 수 있게 도와주는 프레임워크 이다.
물론 적용할 수 있는 곳은 JPA이며, 아쉽게도 start.spring.io 에서 dependency로 추가할 수 없고, 직접 build.gradle에 추가해줘야한다.

Querydsl공식사이트
Querydsl - JPA 튜토리얼 가이드

환경설정 세팅하기

  • queryDSL을 추가하기 위해선 의존성 추가를 해주면 된다.
  • 그리고 entity에 대응되는 Q파일을 생성해야된다.

1번째 방법

  • 이 방법은 컴파일에 other ->compileQuerydsl 이 생성된다.
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.2'
    id 'io.spring.dependency-management' version '1.1.4'
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"

}

group = 'study'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}


dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    //test 롬복 사용
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'


    //Querydsl 추가
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.6.2'


}

tasks.named('test') {
    useJUnitPlatform()
}
clean {
    delete file('src/main/generated')
}

def querydslDir = "$projectDir/build/generated/querydsl"
querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
configurations {
    querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

  • 위와 같이 build.gradle 아래 디렉토리 및 추가하는 코드는 springboot 2.x.x대 버전의 코드이며, 3버전 이후에도 적용이 되긴 하지만,
    • 적용하려면, plugin id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" 을 꼭 추가해줘야 한다.

  • 그럼 Tasks other compileQuerydsl 컴파일에 추가가 됩니다.

2번째 방법

  • 사실 3버전이후는 이걸로 쓰는게 간단하다. 왜냐면, compile에서 build ->build 만 실행해도 모두 적용되 실행되기 때문이다.
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.2'
    id 'io.spring.dependency-management' version '1.1.4'
// 플러그인 없어도 됨.
}

// 동일한 부분 생략

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    runtimeOnly 'com.h2database:h2'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    //test 롬복 사용
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'


    //Querydsl 추가
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.6.2'


}
// 추가된 부분
sourceSets { main { java { srcDirs = ["projectDir/src/main/java", "projectDir/build/generated"] } } }
  • build.gradle을 세팅을하고 컴파일을 돌렸을때, 아래와 같이 build폴더가 생기고 안에 Q파일이 생기면 초기 세팅은 성공이다!


테스트해보기

  • 이제 Querydsl를 세팅한 것일뿐 이제 사용해봐야한다.
  • 사용은 테스트 코드를 작성하여 잘되는지 확인해보자
  • SQL로 저장해보고, 조회해보는 작업을 하는 것이기에, 당연히 DB가 세팅되어있어야하고, JPA도 잘 세팅되어 있어야한다!
  @Test
    public void startQuerydsl() {
            //given
    	//JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(em);
//        QMember member = QMember.member;
//        QMember m = new QMember("m");


        //when
        Member findMember = jpaQueryFactory
                .select(member)
                .from(member)
                .where(
                        member.username.eq("member1")
                ) //파라미터 바인딩 처리
                .fetchOne();

        //then
        assertThat(findMember.getUsername()).isEqualTo("member1");

    }
  • 테스트 결과: 매우 잘 사용되며, JPQL에서는 파라미터 변수를 별도로 만들어 넣어 주는데, QueryDSL은 그런게 없이 바로 파라미터를 넣어주면 잘 실행되는 것을 알 수 있었다.
  • 자동주입으로 JPAQueryFactory를 처리할 수 있고, 그 안에 있는 QMember를 가져와서 사용할 수 있다.

QueryDSL 문법

  • 이제 java코드로 직관적이게 사용할 수 있어졌다.
 member.username.eq("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%’ 검색 
  • fetch() : 리스트 조회, 데이터 없으면 빈 리스트 반환

  • fetchOne() : 단 건 조회

    • 결과가 없으면 : null
    • 결과가 둘 이상이면 : com.querydsl.core.NonUniqueResultException
  • fetchFirst() : 맨 처음 1 건 조회

  • fetchResults() : 페이징 정보 포함, total count 쿼리 추가 실행

  • fetchCount() : count 쿼리로 변경해서 count 수 조회

  • orderBy(member.age.desc(), member.username.asc().nullsLast()) : 정렬 나이 내림차순1순위, 이름 올림차순 2순위 null일 경우 마지막으로 정렬

    • nullsFirst() : null일 경우 처음으로 정렬
  • .fetchResults() : fetch()의 경우 반환값이 List<?>이지만, 이건 QueryResults<?> 이며, 아래 getTotal값을 구하기위해 count()쿼리를 한번더 보내는 작업이 생긴다.

queryResults.getTotal()); // 전체 조회
queryResults.getLimit()); // 조회제한한 갯수
queryResults.getOffset());// offset 한 인덱스
queryResults.getResults(); // list 결과

Group BY

그룹된 상태에서 사용할 수 있는 집계함수도 사용할 수 있다.

  • COUNT(m), //회원수
  • SUM(m.age), //나이 합
  • AVG(m.age), //평균 나이
  • MAX(m.age), //최대 나이
  • MIN(m.age) //최소 나이

당연히 GroupBy도 사용 가능하다.


  List<Tuple> result = queryFactory
             .select(team.name, member.age.avg())
             .from(member)
             .join(member.team, team)
             .groupBy(team.name)
          // .having()
             .fetch();
             

여기까지 하면서 느낀것은 SQL 문법을 할줄 알면, QueryDSL도 직관적으로 금방 습득할만하다는 것이다.
(포스팅이 너무 길어지니 여기서 끊고가고자한다.)
다음엔 SQL 쿼리에서 중요한 Join하는 법과 서브쿼리를 숙지하고 동적쿼리를 어떻게 만드는지 알아보고자 한다.

profile
한단계씩 올라가는 개발자

0개의 댓글