Query 작성법 3. QueryDSL

Yennie·2024년 5월 11일

JPA

목록 보기
11/19

QueryDSL

Hibernate Query Language의 쿼리를 타입에 안전하게 생성 및 관리해주는 프레임워크로, 정적인 타입을 이용하여 SQL 같은 쿼리를 생성할 수 있게 해줌. 자바의 백엔드는 Spring Boot + Spring Data JPA를 함께 사용하는데, 복잡한 쿼리, 동적 쿼리를 구현하는데 한계가 있는데, 이걸 해결하는 것이 QueryDSL임.

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

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

사용이유

  • JPA Query Method을 사용하는 경우 직접적인 연관관계가 없는 entity의 조인이 어려우며, entity의 원하는 필드만 가져올 수 없음 (모든 필드 조회 후 ResponseDTO를 만들어서 반환이 필요함)
  • nativeQuery의 경우 sql문이 매우 복잡해짐

장점

  • 문자가 아닌 코드르 쿼리를 작성할 수 있기 때문에 컴파일 시점에 문법 오류 확인이 가능
  • 복잡한 코드나 동적 쿼리 작성이 편함
  • 제약 조건 등을 메서드 추출을 통해 재사용 가능
  • JPQL 문법과 유사한 형태로 작성 가능

단점

  • 초기 세팅이 어려움
  • JPA 1차 캐시 사용이 불가능함
    -> QueryDSL은 기본적으로 "JPQL을 java코드로 쓸 수 있게 해주는 JPQL 빌더이다. JPQL은 SQL문으로 번역되어서 우선적으로 DB에 다이렉트로 select 문을 쏘는데, JPA를 사용하여 Select하는 경우 영속성 컨텍스트 1차 캐시에 데이터가 저장이되는데, QueryDSL은 이를 무시하고 DB에서 직접 조회해오기 때문
    -> 영속성 컨텍스트에 이미 존재하는 객체와 DB에서 조회해온 객체 아이디가 같을 경우 1차 캐시에 존재하는 데이터를 사용함

사용방법

  1. build.gradle
buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.0.2'
    id 'io.spring.dependency-management' version '1.1.0'
    id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}

group = 'me.yeeunhong'
version = '1.0-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // 스프링 데이터 JPA
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' // 타임리프
    implementation 'org.springframework.boot:spring-boot-starter-validation' // validation
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' // swagger
    implementation("com.querydsl:querydsl-core:${queryDslVersion}")
    implementation("com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta")
    runtimeOnly 'com.h2database:h2' // 인메모리 데이터베이스
    compileOnly 'org.projectlombok:lombok' // 롬복
    annotationProcessor (
            'org.projectlombok:lombok',
            "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta",
            "jakarta.persistence:jakarta.persistence-api:3.1.0"
    )
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

/*
 * queryDSL 설정 추가
 */
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}
sourceSets {
    main.java.srcDir querydslDir
}
compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
    querydsl.extendsFrom compileClasspath
}

test {
    useJUnitPlatform()
}
  1. compileQuerydsl 클릭

클릭 시 'QArticle'이라는 클래스가 생성됨

  1. QueryDslConfig 클래스 만들기

  2. repository package 내 아래 파일 만들기

    BlogRepositoryCustom

package me.yeeunhong.blogproject.repository;

import me.yeeunhong.blogproject.domain.Article;

import java.util.List;

public interface BlogRepositoryCustom {
    List<Article> findByTitle(String title);
}

BlogRepositoryImpl

package me.yeeunhong.blogproject.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import me.yeeunhong.blogproject.domain.Article;

import java.util.List;

import static me.yeeunhong.blogproject.domain.QArticle.article;

@RequiredArgsConstructor
public class BlogRepositoryImpl implements BlogRepositoryCustom {
    private final JPAQueryFactory queryFactory;
    @Override
    public List<Article> findByTitle(String title) {
        return queryFactory.selectFrom(article)
                .where(article.title.eq(title))
                .fetch();
    }
}

BlogRepository

참고
https://velog.io/@jinyeong-afk/%EA%B8%B0%EC%88%A0-%EB%A9%B4%EC%A0%91-QueryDSL%EC%9D%B4%EB%9E%80#:~:text=QueryDSL%EC%9D%80%20%ED%95%98%EC%9D%B4%EB%B2%84%EB%84%A4%EC%9D%B4%ED%8A%B8%20%EC%BF%BC%EB%A6%AC,%EC%83%9D%EC%84%B1%ED%95%A0%20%EC%88%98%20%EC%9E%88%EA%B2%8C%20%ED%95%B4%EC%A4%80%EB%8B%A4.

https://velog.io/@cws0718/Spring-JPA-QueryDsl%EC%9D%B4%EB%9E%80

https://jiny-dev.tistory.com/136

https://batory.tistory.com/496

profile
PM | Aspiring SWE | linkedin.com/in/emilyyeeun

0개의 댓글