이번 시간에는 Spring Data Mongo에 QueryDSL을 사용하여 Mongo Document에 정적 타입을 생성하여 Type-Safety한 방식으로 동적 쿼리를 작성하여 사용할 수 있도록 설정한 경험을 공유하고자 합니다.
현재 작업 중인 프로젝트에서는 이미 Spring Data JPA 와 QueryDSL을 적용해 사용하고 있습니다.
Spring Data JPA와 QueryDSL은 둘 다 Java 기반의 프레임워크로, 데이터베이스와 상호 작용하는데 도움을 주는 기술입니다.
현재 프로젝트에서는 RDB 기반의 데이터베이스 뿐 아니라 NoSQL기반의 데이터베이스를 연동하며 사용중인데 이를 RDB와 동일하게 QueryDSL을 적용하면 기존에 사용하던 장점들을 모두 가져갈 수 있을 것 같아서 도입했습니다.
Spring Data Mongo에 QueryDSL를 함께 적용한 이유는 아래와 같습니다.
QueryDSL은 위와 같은 장점을 가지고 있어서 기존 JPA & QueryDSL 조합에 더해 Mongo & QueryDSL도 적용하게 되었습니다.
QueryDSL을 설정하는데에는 생각보다 다양한 방식이 존재합니다.
각 프로젝트의 버전에 따라서 여러 방식으로 설정이 가능합니다.
해당 환경인 경우, Annotation processor 기반의 설정 방법을 이용할 수 없어서 조금 더 복잡한 설정 과정을 거칩니다.
위와 같은 환경에서는 ewerk 플러그인을 사용하여 설정합니다.
plugins {
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
implementation 'com.querydsl:querydsl-mongodb'
implementation 'com.querydsl:querydsl-apt'
}
def querydslGeneratedDir = "$buildDir/generated/querydsl" as Object
querydsl {
library = "com.querydsl:querydsl-apt"
springDataMongo = true
// jpa = true
querydslSourcesDir = querydslGeneratedDir
}
sourceSets {
main.java.srcDir querydslGeneratedDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
Gradle 버전이 증가되면서 Querydsl의 QClass 생성 방법이 변경되다보니 Gradle과 IntelliJ가 업데이트 될때 마다 새로운 설정 방법이 필요해지기도 한다고 합니다.
이로 인해서 Annotation processor 기반 설정 방식을 많이 쓰게 됩니다.
저희가 사용하는 프로젝트에서는 Annotation processor 기반 설정이 가능해서 ewerk 플러그인을 사용하지 않고 QClass 빌드에 대해 별도 설정 없이 간편하게 빌드하여 사용하였습니다.
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies{
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb’
// Additional required dependencies
// ...
// QueryDSL
implementation 'com.querydsl:querydsl-core:5.0.0'
implementation 'com.querydsl:querydsl-mongodb:5.0.0'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
}
querydsl-apt:jpa
모듈의 JPAAnnotationProcessor 기반 설정으로 QClass을 생성합니다.
JPAAnnotationProcessor 기반 설정의 경우 도큐먼트 클래스에 @Entity
를 명시해주어야 빌드 시점에 QClass를 자동으로 생성할 수 있습니다.
@Document 관련 패키지의 어노테이션 설정과 호환이 되지 않을 수 있습니다.
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies{
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb’
// Additional required dependencies
// ...
// QueryDSL
implementation 'com.querydsl:querydsl-core:5.0.0'
implementation 'com.querydsl:querydsl-mongodb:5.0.0'
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0'
annotationProcessor 'org.springframework.boot:spring-boot-starter-data-mongodb'
}
// 컴파일러 옵션에 annotationProcessor 직접 지정
compileJava {
options.compilerArgs += [
"-processor", 'lombok.launch.AnnotationProcessorHider$AnnotationProcessor,org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor'
]
}
Lombok을 사용하는 경우
lombok.launch.AnnotationProcessorHider$AnnotationProcessor
를 추가해주어야합니다.
빌드를 시작하게 되면 MongoAnnotationProcessor가 실행된다는 메시지와 함께 Document 클래스들의 QClass가 생성됩니다.
참고 : MongoAnnotationProcessor를 사용하는 경우
@Document
어노테이션이 붙지 않은 클래스도 모두 Q타입이 생성됩니다.
@Document
가 붙은 클래스는 EntityPathBase를 상속 받아서 생성- 그외 클래스는 BeanPath을 상속 받아서 생성
그외 @QueryEntity 와 mysema 패키지 기반 설정 (Maven)
https://www.baeldung.com/queries-in-spring-data-mongodb#querydsl-queries
프로젝트를 빌드하게 되면 위와 같이 build/generated/sources/annotationProcessor
경로에 JPA를 사용할때와 동일하게 QClass이 생성됩니다.
QClass 클래스 추가 완료
여기서 한가지 추가로 고려해야할 부분이 있는데요.
해당 모듈에서 생성된 QClass을 실제로 사용하는 모듈의 소스에는 반드시 아래 의존성을 추가해주어야 의존성 충돌을 방지 할 수 있습니다.
implementation('com.querydsl:querydsl-mongodb') {
exclude group: 'org.mongodb', module: 'mongo-java-driver'
}
아래와 같이 사용하면 됩니다.
아래 2가지 의존성을 사용하여 Mongo 쿼리를 생성하였습니다.
QuerydslRepositorySupport
, SpringDataMongodbQuery
Gradle: org.springframework.data:spring-data-mongodb:3.3.0
- com.querydsl:querydsl-mongodb:5.0.0
- org.mongodb:mongodb-driver-core:4.4.0
- org.mongodb:mongodb-driver-sync:4.4.0
아쉽게도 MongoDB의 QueryDSL은 기존 JPA의 QueryDSL과 다르게 다양한 기능을 지원하지 않습니다.
따라서, 복잡한 쿼리 수행을 위해서는 기존 MongoTemplate을 사용해야 합니다.
| Aggregate, Document에 타입 지정 안된 Object 필드의 경우 등등
MongoTemplate을 사용하게 되면 Type-Safe하지 못한 단점을 가져가게 되는데 우리에겐 위에서 생성한 QType 정보를 가지고 있기 때문에 QType 정보 기반으로 path 정보를 잘 파싱해서 사용하면 Type-Safe하게 MongoTemplate 사용이 가능합니다.
여러 예제를 찾다보면 com.mysema.querydsl:querydsl-mongodb
모듈 사용하게 끔 나와 있는 예제를 찾아볼 수 있어서 궁금해서 어떤 차이가 있는지 찾아보았는데 각 패키지의 차이점은 버전 차이입니다.
com.mysema.querydsl
: querydsl 3.7.4가 마지막 버전com.querydsl
: querydsl 4버전 이후Spring Data MongoDB
는 더 많은 통합 및 Spring 기반 기능을 제공하며, Morphia
는 객체 매핑에 중점을 둔 경량 라이브러리입니다.
두가지 모두 ODM (Object Document Mapper) 라이브러리입니다.
Spring Data MongoDB
Morphia
Morphia 예시 코드
// Morphia 인스턴스 생성
Morphia morphia = new Morphia();
// 데이터 스토어 설정
Datastore datastore = morphia.createDatastore(mongoClient, "your_database_name");
// 조회 예제: 사용자(User) 컬렉션에서 나이가 25 이상인 모든 문서를 가져오기
List<User> users = datastore.createQuery(User.class)
.field("age").greaterThanOrEq(25)
.asList();
또 다른 ODM 라이브러리
MongoJack : JSON에서 MongoDB 객체로의 직접 매핑 제공
querydsl-apt
에서 제공하는 JPA 방식과는 일부 다른 Morphia apt 모듈
MorphiaAnnotationProcessor 를 사용하여 Morphia 관련 어노테이션 설정들을 기반으로 QClass를 생성해주는 역할을 하는 듯합니다.
Spring Boot Reactive Mongo Data QueryDSL
Spring Boot 3 Querydsl 설정 공유
A Guide to Queries in Spring Data MongoDB
Spring Boot, Spring Data MongoDB, Querydsl로 타입 세이프 쿼리 작성하기
How to querydsl with spring-data-mongodb and gradle
gradle 프로젝트에서 querydsl 설정하기
큰 도움 얻고 갑니다!! 감사합니다!