소통하는 개발자 Sean
article thumbnail

queryDSL에 대해 더 자세히 알고 싶다면, 1편을 보고오시는것을 추천드립니다!

2023.11.03 - [분류 전체보기] - queryDSL이란? 간단한 설명과 사용이유

 

queryDSL이란? 간단한 설명과 사용이유

👉 글을 쓰게 된 이유 저는 JPA가 복잡한 쿼리를 효율적이고 편리한 개발을 지원하지만 특히 복잡한 쿼리를 작성할 때 일부 단점이 있다고 생각했습니다. JPA는 기본적으로 단순한 CRUD 쿼리와 간

sean-lets-go.tistory.com

 

👉 Gradle 의존성과 세팅

plugins {
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10' // Q 클래스 생성 플러그인
    // ...
}

def querydslDir = "$buildDir/generated/querydsl" // 생성된 Q 클래스가 저장될 위치를 정의한다.

// queryDSL 자체에 대한 config
querydsl { 
    jpa = true // jpa = true로 설정하여 JPA 애너테이션을 인식
    querydslSourcesDir = querydslDir // querydslSourcesDir를 querydslDir로 설정하여 Q 클래스에 대한 output 디렉토리를 지정
}


sourceSets {
    main.java.srcDir querydslDir // Q 클래스 등이 저장되는 querydslDir를 소스 코드가 저장되는 디렉토리로 등록
		// 이 덕에 Gradle이 자동으로 QueryDSL 코드를 컴파일할 수 있다.
}

configurations {
    querydsl.extendsFrom compileClasspath // querydsl 클래스 경로를 컴파일 클래스 경로에 상속
}

// 내부적으로 Querydsl은 Java의 Annotation Processor를 이용한다.
// 컴파일러는 이 경로에 위치한 모든 JAR 파일들을 검색하여 annotation processor를 찾는다. 
// 그리고 이를 실행하여 소스 코드에서 정의한 어노테이션들을 처리한다.
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

dependencies {
		//...
		implementation "com.querydsl:querydsl-codegen:5.0.0" // QueryDSL 코드 생성
		implementation "com.querydsl:querydsl-jpa:5.0.0" // JPA 지원
		implementation "com.querydsl:querydsl-apt:5.0.0" // Annotation Process Tool
		//...
}

// -proc:only: 이 옵션은 컴파일러의 어노테이션 처리만 수행하고, 클래스를 실제로 컴파일하지 않는 것을 의미한다.
// -processor: 이 옵션은 어노테이션 프로세서를 지정하는데, QueryDSL의 어노테이션 프로세서와 Lombok의 어노테이션 프로세서를 지정하여 명시적으로 설정한다.
project.afterEvaluate {
    project.tasks.compileQuerydsl.options.compilerArgs = [
            "-proc:only",
            "-processor", project.querydsl.processors() +
                    ',lombok.launch.AnnotationProcessorHider$AnnotationProcessor'
    ]
}

// Java 컴파일 작업이 compileQuerydsl 작업에 의존하도록 설정한다.
// compileQuerydsl 작업이 먼저 수행된 후에 compileJava 작업이 수행되게끔 순서를 정해주는 것인데,
// QueryDSL Q-Type 소스 생성 작업이 Java 컴파일 작업 전에 먼저 이뤄지도록 한다.
// 위 afterEvaluate와 같이 사용하면, Lombok 어노테이션 프로세스와 충돌하지 않는다.
tasks.named('compileJava') {
    dependsOn tasks.named('compileQuerydsl')
}

 

👉 Repository

  • 우리는 JpaRepository의 메서드(save, find)를 사용하면서 동시에 Custom(우리가 만들 메소드)한 QueryDSL을 사용해야 합니다.
  • 그러기 위해선 다소 헷갈릴 수도 있지만, 관계를 잘 설정해주어야 합니다.

Book에 관해 나와있지만 관계만 봐주세요

  • 스프링은 JpaRepository 인터페이스를 상속받는 Repository 인터페이스를 찾으면, 이를 구현하는 프록시 객체를 생성합니다.
    • Spring Data JPA는 인터페이스에 대한 프록시 객체를 생성할 때 해당 인터페이스와 그것이 상속하는 인터페이스의 모든 기능을 포함합니다.
    • 밑의 코드를 보면, 스프링은 JpaRepository를  상속받은 -> ContractRepository의 구현 프록시 객체를 만들겁니다.  이때에 Jpa 기본 CRUD 기능 뿐만 아니라, 같이 상속받은 'ContractRepositoryCustom'의 기능도 포함해서 프록시 객체를 만들것입니다.
public interface ContractRepository extends JpaRepository<Contract, Long>, ContractRepositoryCustom {}

 

👉 성공!

 

👉 JPQL -> queryDSL

  • 이러한 장점으로 인해 JPQL을 queryDSL로 변경했습니다.
    • 문자가 아닌 코드로 쿼리를 작성할 수 있어 컴파일 시점에 문법 오류를 확인할 수 있습니다.
    • 인텔리제이와 같은 IDE의 자동 완성 기능의 도움을 받을 수 있습니다.
    • 복잡한 쿼리나 동적 쿼리 작성이 편리합니다.
    • 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있습니다 .
    • JPQL 문법과 유사한 형태로 작성할 수 있어 쉽게 적응할 수 있습니다 .

JPQL
queryDSL

  • 많이 복잡한 쿼리가 아니였는데도, IDE의 도움을 받을 수도 있고 직관적으로 변한것 같아 만족합니다.

 

👉 각 속도 비교

queryDSL 사용

  • 속도만 보면, queryDSL을 사용한것이 제일 느립니다...
  • 제가 설정을 잘못한것일 수도 있겠습니다.
    queryDSL의 성능이슈는 시간이되면 다시한번 다뤄보겠습니다!

 

이제는 .. JPA에서 복잡한 쿼리도 문제 없습니다!!
profile

소통하는 개발자 Sean

@Sean-creative

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!