JVM/JPA

[JPA] QueryDSL 소개 및 프로젝트 설정하기

헹창 2023. 5. 25.
반응형

QueryDSL이란?

QueryDSL은 하이버네이트 쿼리 언어(HQL: Hibernate Query Language)의 쿼리를 타입에 안전하게 생성 및 관리해주는 프레임워크이다.

QueryDSL은 정적 타입을 이용하여 SQL과 같은 쿼리를 생성할 수 있게 해 준다.

 

자바 백엔드 기술은 Spring Boot와 Spring Data JPA를 함께 사용한다. 하지만, 복잡한 쿼리, 동적 쿼리를 구현하는 데 있어 한계가 있다. 이러한 문제점을 해결할 수 있는 것이 QueryDSL이다.

 

QueryDSL이 등장하기 이전에는 Mybatis, JPQL, Criteria 등 문자열 형태로 쿼리문을 작성하여 컴파일 시에 오류를 발견하는 것이 불가능했다.

하지만, QueryDSL은 자바 코드로 SQL 문을 작성할 수 있어 컴파일 시에 오류를 발생하여 잘못된 쿼리가 실행되는 것을 방지할 수 있다.

QueryDSL vs JPQL

Spring Data JPA가 기본적으로 제공하는 CRUD 메서드 (혹은 쿼리 메서드) 기능을 사용하더라도, 원하는 조건의 데이터 수집을 위해서는 JPQL을 작성하게 된다. 간단한 로직을 작성하는 데에 큰 무리는 없으나 복잡한 로직의 경우, 개행이 포함된 쿼리 문자열이 상당히 길어질 뿐더러 JQL 문자열에 오타 혹은 문법적 오류가 존재할 경우 정적쿼리라면 어플리케이션 로딩 시점에 이를 발견할 수 있지만 그 외는 런타임 시점에 에러가 발생한다

 

Spring Data JPA Method

List<Member> findAllByUserName(String userName);

JPQL

String username = "haenny";
String jpql = "select m from Member m where m.username = :username";
List<Member> result = em.createQuery(query, Member.class).getResultList();

QueryDSL

String username = "haenny";

List<Member> result = queryFactory
        .select(member)
        .from(member)
        .where(usernameEq(username))
        .fetch();

QueryDSL 장점

  • 문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있다.
  • 자동 완성 등 IDE의 도움을 받을 수 있다.
  • 동적인 쿼리 작성이 편리하다.
  • 쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있다.

QueryDSL 프로젝트 설정하기

기존에 작성한 @Entity 클래스들은 QueryDSL에서 사용하기 위해서는 Q-Type 클래스로 변환되어야한다.

생성된 Q 클래스들은 QueryDSL의 쿼리 작성 시에 사용되게 된다.

 

QueryDSL 을 사용하기 위한 설정이 조금 까다롭기 때문에, 오늘은 build.gradle 설정하는 부분까지만 기록하려고 한다.

QueryDSL build.gradle 설정 

// 1. querydsl version 정보 추가
buildscript {
    ext {
        queryDslVersion = '5.0.0'
    }
}    
// 2. querydsl plugins 추가
plugins {
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
    // ...
}
// 3. querydsl dependencies 추가
dependencies {
    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
    annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
    // ...
}
// 4. querydsl 설정 추가
// 4-1. querydsl 에서 사용할 경로 설정
def querydslDir = "$buildDir/generated/querydsl"
// 4-2. JPA 사용 여부와 사용할 경로를 설정
querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
// 4-3. build 시 사용할 sourceSet 추가
sourceSets {
    main.java.srcDir querydslDir
}
// 4-4. querydsl 이 compileClassPath를 상속하도록 설정
configurations {
    querydsl.extendsFrom compileClasspath
}
// 4-5. query 컴파일 시 사용할 옵션 설정
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

필요한 라이브러리 의존성 추가

참고로, QueryDSL 5 버전 이후부터는 querydsl-jpa 와 querydsl-apt 의존성에 버전을 명시해주어야한다.

  • querydsl-jpa : QueryDSL JPA 라이브러리
  • querydsl-apt : 엔티티 클래스를 쿼리 타입(Q) 클래스로 생성할 때 필요한 라이브러리

 

gradle을 빌드 툴 도구로서 사용하는 나에게 각 항목의 역할을 간략히 정리해보자..

  • plugins {} : 프로젝트 빌드 시 필요한 여러 과정들을 task로 포함하고 있는데, 빌드 시 이 task들이 내부로 실행되어지기 위해 추가되는 설정
  • def queryDslDir : 실제 @Entity 클래스들이 QueryDSL 플러그인에 의해 변환된 Q클래스들을 생성시키는 경로 지정
  • querydsl {} : QueryDSL 플러그인이 참조하는 블럭임을 여기에 설정된 값으로 QueryDSL 빌드를 해주는 것으로 추정
  • sourceSets {} : gradle build 시 src/main/java 디렉토리 파일들만 컴파일이 되지만, QueryDSL Q 클래스와 같이 다른 폴더에 있는 파일들을 빌드 항목에 추가하기 위해 사용되는 블럭
  • compileQuerydsl {} : QueryDSL을 컴파일하기 위한 설정 블럭으로 querydsl-apt 모듈의 annotationProcessor 경로를 지정해준다.
  • configurations {} : querydsl의 컴파일된 classpath 경로 지정

 

QueryDSL QType compile

Gradle - Tasks - other - compileQuerydsl 을 더블 클릭하면, queryDslDir 설정한 경로에 QType (쿼리타입)의 쿼리용 클래스가 생성될 것이다.

 

QueryDSL 코드 

JPAQueryFactory 객체 생성

@PersistenceContext
EntityManager em;

...
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
...
  • QueryDSL을 사용하려면 com.mysema.query.jpa.impl.JPAQueryFactory 객체를 생성해야 한다.
    이 때 EntityManager을 생성자에 넣어주면 해당 EntityManager을 이용해 쿼리를 전송한다.
  • JPAQueryFactory객체를 생성하면 QueryDSL을 사용할 준비는 완료된다.

QType 선언 방법

생성자를 사용해 선언

...
QMember qMember = new QMember("member1");
...

 

  • member1 : 쿼리를 실행할 때 alias를 구분할 수 있으며, 하나의 쿼리에 두 가지 이상의 Member 테이블을 이용할 때 구분하기 위해 사용한다

QMember 클래스에 정적으로 생성된 QMember를 가져온다

 public class QMember extends EntityPathBase<Member> {
    ...
    public static final QMember member = new QMember("member1");
    ...
  }
...
QMember qMember = QMember.member;
...

 

 

728x90
반응형

댓글

추천 글