Spring Data JPA를 사용한 Spring Boot 프로젝트 기반으로 코드를 작성할 때
Command 작업은 큰 문제는 없지만 집계와 같은 Query 성 작업은 JPA 로는 어느정도 한계가 있다.
JPQL을 사용할 순 있겠지만 String base로 코드가 작성되기 때문에 작업을 하면서 실수가 날 여지가 있고 직접 실행하지 않는 이상 이 실수를 알기 힘든 경우가 있다.
Querydsl 를 사용하면 이러한 JPQL의 단점을 보완할 수 있다.
Querydsl 에서 생성해준 컴파일 된 코드 기반으로 쿼리를 작성하기 때문에 일단 쿼리 작성 자체가 매우 쉬워지고 컴파일 시점에서 오류 또한 찾을 수 있다.
이번 포스팅은 Kotlin 기반의 Spring Boot 프로젝트에 JPA 기반 Querydsl 을 세팅하고 간단한 사용법에 대해 알아보도록 하겠다.
해당 코드는 여기에서 확인할 수 있다.
Project 생성 및 Entity 생성
단순하게 Spring Data JPA 와 MySQL Driver dependency만 추가하고 Spring Boot 프로젝트를 생성한다.
그리고 단순하게 제품(Product)를 가리키는 Entity와 이에 대한 Repository를 추가한다.
import java.time.LocalDateTime
import javax.persistence.*
@Entity(name = "products")
data class Product(
@Column(name = "name")
val name: String,
@Column(name = "quantity")
var quantity: Long,
@Column(name = "registeredAt")
val registeredAt: LocalDateTime,
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null
)
import org.springframework.data.jpa.repository.JpaRepository
interface ProductRepository : JpaRepository<Product, Long>
Querydsl 설정
먼저 build.gradle.kts 파일 설정이다.
기본적으로 Spring Initializr 에서 설정된 것이고
추가 설정 이라고 표시해둔 곳만 추가해주면 된다.
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.5.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.10"
kotlin("plugin.spring") version "1.5.10"
kotlin("plugin.jpa") version "1.5.10"
kotlin("kapt") version "1.3.61" // <= 추가 설정
}
group = "com.tistory.devs0n"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
val qeurydslVersion = "4.4.0" // <= 추가 설정
repositories {
mavenCentral()
}
// 아래 블록 추가 설정
sourceSets["main"].withConvention(org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet::class) {
kotlin.srcDir("$buildDir/generated/source/kapt/main")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// db
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("mysql:mysql-connector-java")
// querydsl (추가 설정)
implementation("com.querydsl:querydsl-jpa:$qeurydslVersion")
kapt("com.querydsl:querydsl-apt:$qeurydslVersion:jpa")
kapt("org.springframework.boot:spring-boot-configuration-processor")
// test
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
그 다음 Querydsl 에서 생성해주는 Q class 를 확인하기 위해 gradle build 를 하면 다음과 같이 build 디렉토리 내에서 QProduct 를 확인할 수 있다.
그리고 Querydsl을 편리하게 쓰기위해 다음과 같이 JPAQueryFactory 를 bean으로 등록하는 QuerydslConfig 를 생성한다.
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import javax.persistence.EntityManager
import javax.persistence.PersistenceContext
@Configuration
class QuerydslConfig(
@PersistenceContext // <- annotation 확인!
private val entityManager: EntityManager
) {
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(this.entityManager)
}
}
Test
이제 기본적인 세팅은 끝났다.
테스트를 통해 실제로 Querydsl이 잘 동작하는지 확인해보자.
다음과 같이 products 테이블(Product entity)에 데이터가 쌓여있을 때
> 1개월 내로 등록된 Product 중 수량이 적은 5개 찾기
라는 기능이 필요할 때 작성되는 SQL query는 다음과 같겠다.
SELECT
FROM products
WHERE DATE_SUB(NOW(), INTERVAL 1 MONTH) < registeredAt
ORDER BY quantity ASC
LIMIT 5
이제 이 SQL 문을 Querydsl 을 사용하여 작성하면 아래와 같다.
// when
val products = jpaQueryFactory // QuerydslConfig에서 bean으로 등록한 JPAQueryFactory
.selectFrom(product)
.where(product.registeredAt.after(LocalDateTime.now().minusMonths(1)))
.orderBy(product.quantity.asc())
.limit(5)
.fetch()
// then
assertThat(products).hasSize(5)
assertThat(products[0].name).isEqualTo("Ginger")
assertThat(products[1].name).isEqualTo("Fish and Chips")
assertThat(products[2].name).isEqualTo("Egg")
assertThat(products[3].name).isEqualTo("Dragon Fruit")
assertThat(products[4].name).isEqualTo("Cap")
(전체 테스트 코드는 여기를 확인)
그리고 이 테스트 코드를 실행하면 테스트가 통과되고 다음과 같이 SQL query를 로그를 통해 확인할 수 있는데
SQL 문에서는 DATE_SUB 함수를 사용했지만 Querydsl 을 사용할 때는 날짜를 직접 계산할 수 있기 때문에 where 문이 조금 다르다.
'Java & Kotlin > Spring' 카테고리의 다른 글
Spring Security 인증 기본 구조 (0) | 2021.07.18 |
---|---|
Spring Kafka 간단하게 맛보기 (0) | 2021.07.11 |
Rest Assured 테스트 시 redirect 막기 (0) | 2021.06.03 |
Github Actions로 PR 시 테스트를 돌려보자 (7) | 2021.04.21 |
MySQL Docker container를 이용한 통합 테스트 시 한글 깨짐 이슈 (0) | 2021.04.07 |
댓글