Querydsl
Spring을 사용하는 백엔드 개발자라면 Querydsl이라는 키워드를 많이 들어봤을 것이다. JPA를 사용하면 데이터베이스의 테이블을 Java 코드로 Entity를 만들어 관리하게 된다. Spring에서는 Spring Data JPA를 활용해서 기본적인 CRUD를 사용할 수 있지만 조금만 복잡해지면 JPQL을 사용해야 한다.
JPQL의 간단한 예시를 보자
Person(Entity)
@Entity
class Person(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
@Column(name = "name")
var name: String? = null,
@Column(name = "age")
var age: Int? = null,
@Column(name = "job")
var job: String? = null
)
PersonRepository
interface PersonRepository: JpaRepository<Person, Long> {
@Query("select p.id from Person p where p.age between :from and :to")
fun findIdByAgeBetween(@Param("from") from: Int, @Param("to") to: Int)
}
@Query 어노테이션을 활용하면 nativeQuery 옵션을 true로 줘서 SQL도 사용할 수 있지만 생략하거나 false로 준다면 JPQL을 사용할 수 있다.
전반적으로 JPQL은 문자열로 작성되기 때문에 사람이 작성하는 특성 상 오타가 없을 거라는 확신은 가질 수 없다. 위 예제에서는 나이가 from과 to 사이인 사람의 id를 조회하는 메서드인데 파라미터 바인딩 시 :from
대신 :fron
으로 들어갈 수도 있고, 이는 컴파일 오류를 내지 않기 때문에 런타임 시점에 해당 메서드가 사용될 때 잘못 됐다는 것을 알 수 있다. 그리고 이 예제는 정적 쿼리이기 때문에 간단하게 보일 수 있다. 동적 쿼리를 JPQL로 짠다면 코드가 복잡해진다. (사람마다 다르겠지만 내 기준으로는 가독성이 떨어져 보인다.)
Querydsl 장점
- 문자열이 아닌 Java 코드로 쿼리를 작성할 수 있다.
- 컴파일 시점에 오류를 확인할 수 있다.
- 동적 쿼리 작성이 용이하다.
이러한 이점으로 많은 사람들이 Querydsl을 사용한다.
Querydsl 설정
Kotlin, Spring Boot 기준으로 예제 코드를 준비했다.
개발 환경
- Kotlin / Spring Boot
- Gradle (Kotlin dsl)
build.gradle.kts
plugins {
id("org.springframework.boot") version "2.7.3"
id("io.spring.dependency-management") version "1.0.13.RELEASE"
kotlin("jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
kotlin("kapt") version "1.6.21" // kapt 추가
}
...
// querydsl version
val querydslVersion = "5.0.0"
dependencies {
...
// querydsl
implementation("com.querydsl:querydsl-jpa:$querydslVersion")
kapt("com.querydsl:querydsl-apt:$querydslVersion:jpa")
}
KAPT
이 프로젝트는 Kotlin을 사용하기 때문에 컴파일을 javac로 하지 않고 kotlinc로 컴파일한다. 따라서 Java로 작성 된 Annotation Processor가 동작하지 않고, Kotlin에서는 KAPT(Kotlin Annotation Precessing Tool)을 사용한다.
QuerydslConfiguration
@Configuration
class QuerydslConfiguration {
@PersistenceContext
private lateinit var entityManager: EntityManager
@Bean
fun jpaQueryFactory(): JPAQueryFactory {
return JPAQueryFactory(entityManager)
}
}
gradle의 compileKotlin
을 해보면
Q파일이 build/generated/source/kapt/...
경로에 생성되었다.