๐ชด 0. ๋ค์ด๊ฐ๊ธฐ ์
MongoDB๋ ์คํค๋ง๊ฐ ์๋ ๋น๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก, ORM์ฒ๋ผ ๊ด๊ณ๋ฅผ ๋งคํํ๋ ๋ฐฉ์์ด ์๋๋๋ค.
๋ฐ๋ผ์ SQL์ด ์๋ JSON ๊ธฐ๋ฐ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์ด๋ฅผ ๊ฐ์ฒด๋ก ๋ค๋ฃจ๊ธฐ ์ํด Criteria๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๊ทธ๋ฌ๋ Criteria๋ฅผ ์ฌ์ฉํ์ ๋, ํ๋๋ช ์ ์ค์๋ก ์๋ชป ์์ฑํ๋๋ผ๋ ์ปดํ์ผ ์ ์๋ฌ๋ฅผ ํ์ธํ ์ ์๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์์๋ MongoDB์์ Criteria๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , Qclass ์ QueryDSL์ ์ ์ฉํ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ค๋ณด๊ฒ ์ต๋๋ค.
๐ชด 1. ๊ธฐ์กด์ Criteria ์ฝ๋
Criteria ๋ JSON ๊ธฐ๋ฐ์ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ฒด๋ก ์์ฑํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฐฉ์์ ๋๋ค.
๋น๊ต๋ฅผ ์ํด ๋จผ์ Criteria๋ก ์์ฑ๋ ์์ ์ฝ๋๋ฅผ ๋ณด์ฌ๋๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค.
@Slf4j
@Primary
@RequiredArgsConstructor
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final ReactiveMongoOperations mongoOperations;
@Override
public Mono<Boolean> updateMemberPassword(String email, String newPassword, ServerWebExchange exchange) {
Query query = new Query(Criteria.where("email").is(email));
Update update = new Update()
.set("password", newPassword)
.set("updatedAt", LocalDateTime.now());
return mongoOperations.updateFirst(query, update, Member.class)
.map(updateResult -> updateResult.getModifiedCount() > 0)
.defaultIfEmpty(false);
}
}
์์ ์ฝ๋๋ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฐ๋จํ ์์ ์ฝ๋์ ๋๋ค.
์ด ์ฝ๋์์ "email" ์ ๋ฌธ์์ด๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์, ์ค์ ํ๋๋ช ๊ณผ ๋ค๋ฅด๊ฒ ์์ฑํ๋๋ผ๋
์ปดํ์ผ ๊ณผ์ ์์๋ ๋ฐ๊ฒฌํ ์ ์๋ค๋ ์น๋ช ์ ์ธ ๋ฌธ์ ์ ์ด ์์ต๋๋ค.
๐ชด 2. Criteria ๋นผ๊ณ Qclass + QueryDSL ์ ์ฉํ๊ธฐ
ํ์ฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ์๋์ ๊ฐ์ต๋๋ค.
JDK 21, Spring Boot 3.2.2, MongoDB driver 4.11.2 |
ํ๋ก์ ํธ ํ๊ฒฝ์ ๋ง๋ QueryDSL๋ฒ์ ์ผ๋ก ์ค์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
์๋์ ์ค์ ํ์ผ์ ์ฐธ๊ณ ํ์ฌ ์์ ์ ๊ฐ๋ฐํ๊ฒฝ์ ๋ง๋๋ก ์ฌ๋ฌ ์ค์ ์ ์ถ๊ฐํด์ค๋๋ค.
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
ext {
queryDslVersion = "5.0.0"
}
dependencies {
// QueryDSL
implementation "com.querydsl:querydsl-mongodb:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
// MongoDB
implementation 'org.mongodb:mongodb-driver-reactivestreams:4.11.2'
implementation 'org.mongodb:mongodb-driver-core:4.11.2'
configurations.all {
exclude group: "org.mongodb", module: "mongo-java-driver" // ๊ธฐ์กด MongoDB Java ๋๋ผ์ด๋ฒ๋ฅผ ์ ๊ฑฐ(์ถฉ๋ ๋ฐฉ์ง)
}
}
// QueryDSL์ด ์์ฑํ ์ฝ๋์ ์ถ๋ ฅ ๋๋ ํฐ๋ฆฌ ์ค์ (Qclass ์์ฑ ๊ฒฝ๋ก)
def querydslDir = "${buildDir}/generated/querydsl"
// ์ด๋
ธํ
์ด์
ํ๋ก์ธ์ ์ค์ (์ถ๊ฐ ์ค๋ช
์์ )
tasks.withType(JavaCompile).configureEach {
options.annotationProcessorPath = configurations.annotationProcessor
}
// QueryDSL ์ฝ๋ ์์ฑ์ ์ํ JavaCompile Task
task generateQueryDSL(type: JavaCompile, group: 'build') {
source = sourceSets.main.java
classpath = configurations.compileClasspath
destinationDirectory = file(querydslDir)
options.annotationProcessorPath = configurations.annotationProcessor
options.compilerArgs = [
"-proc:only"
]
}
compileJava {
dependsOn clean
}
clean {
delete file(querydslDir)
}
์ค์ ์ ๋ง์น ํ์, Document ๋ฅผ ์ ์ํ ๊ณณ์ ์๋์ฒ๋ผ @QueryEntity ๋ฅผ ์ถ๊ฐ๋ก ์ ์ธํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
์์ ์ค์ ์์ ์ด๋ ธํ ์ด์ ํ๋ก์ธ์(annotation processor)๋ฅผ ์ค์ ํด์ฃผ์ง ์๋๋ค๋ฉด @QueryEntity๋ฅผ ์์ฑํด๋ Qclass๊ฐ ์์ฑ๋์ง ์์ต๋๋ค.
import com.querydsl.core.annotations.QueryEntity;
@QueryEntity //์ถ๊ฐํด์ผ ํ ์ด๋
ธํ
์ด์
@Document
public record Member(
){}
๊ทธ๋ฆฌ๊ณ ๋น๋๋ฅผ ํ๊ฒ๋๋ฉด ์์์ ์ค์ ํ๋ ๊ฒฝ๋ก์ Qclass๊ฐ ์๋์ผ๋ก ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๐ชด 3. ์ด๋ป๊ฒ ์ฌ์ฉํ ๊น?
๋จผ์ , QueryPathResolver ํด๋์ค๋ฅผ ์์ฑํด์ค๋๋ค.
import com.querydsl.core.types.Path;
import org.springframework.stereotype.Component;
@Component
public class QueryPathResolver {
public static String get(Path<?> path) {
return path.getMetadata().getName();
}
}
๊ทธ๋ฆฌ๊ณ , 1๋ฒ์ ๊ธฐ์กด ์ฝ๋๋ฅผ ์๋์ฒ๋ผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
"email" -> QueryPathResolver.get(MEMBER.email) |
@Slf4j
@Primary
@RequiredArgsConstructor
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final ReactiveMongoOperations mongoOperations;
private final QMember MEMBER = QMember.member; //์ถ๊ฐ
@Override
public Mono<Boolean> updateMemberPassword(String email, String newPassword, ServerWebExchange exchange) {
Query query = new Query(Criteria.where(QueryPathResolver.get(MEMBER.email)).is(email));
Update update = new Update()
.set(QueryPathResolver.get(MEMBER.password), newPassword)
.set(QueryPathResolver.get(MEMBER.updatedAt), LocalDateTime.now());
return mongoOperations.updateFirst(query, update, Member.class)
.map(updateResult -> updateResult.getModifiedCount() > 0)
.defaultIfEmpty(false);
}
}
๊ตณ์ด QueryPathResolver ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ ์ด์ ๋
QueryDSL์์ QMember.email ๊ฐ์ ํ๋๋ ์ง์ ์ ์ผ๋ก ๋ฌธ์์ด์ด ์๋๋ผ, Path ๊ฐ์ฒด๋ก ํํ๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
์๋์ Qclass ๋ฅผ ์ค์ ๋ก ํ์ธํด๋ณด๋ฉด String ํ์ ์ด ์๋ Path ๊ฐ์ฒด๋ก ํํํ๊ณ ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
public class QMember extends EntityPathBase<Member> {
public static final QMember member = new QMember("member");
public final StringPath email = createString("email");
public final StringPath password = createString("password");
}
๐ชด 4. ๋ง๋ฌด๋ฆฌ
์ด๋ฒ ํฌ์คํ ์ '์ปดํ์ผ ์๋ฌ ๋ฐฉ์ง'์ '์ฝ๋ ์ผ๊ด์ฑ'์ ์ ์งํ ์ ์๋ค๋ ์ฅ์ ์ด ์๊ธฐ๋ ํ์ง๋ง,
์์งํ MongoDB์์ queryDSL์ ์ ์ฉํ๋ฉด ์ฅ์ ์ด ๋ง์ง๋ ์์ต๋๋ค.
MongoDB์์๋ ์ค์ฒฉ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๊ธฐ๋ ํ๋๋ฐ queryDSL์ ์ด๋ฐ ๋ถ๋ถ์ ์๋ฒฝํ๊ฒ ์ง์ํด์ฃผ์ง ์์์ ์ง์ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค.
(์๋ ์์์ฝ๋์ฒ๋ผ ์์๊ฒฝ๋ก + "." + ํ์๊ฒฝ๋ก ์ ํํ๋ก ์์ฑ์ ํด์ผํฉ๋๋ค.)
Member member = mongoOperations.findOne(
query(where(QueryPathResolver.get(Member.groupMemberInfoList) + "." + QueryPathResolver.get(MEMBER_INFO.memberId)).is(memberId).and(QueryPathResolver.get(Member.id)).is(meberId)),
Member.class
);
๊ทธ๋ฆฌ๊ณ queryDSL์ SQL์ ์ต์ ํ๊ฐ ๋์ด์์ด์ MongoDB์ฒ๋ผ Aggregation Framework๋ฅผ ์ฌ์ฉํ ๋์๋
$lookup, $unwind, $group ์ ๊ฐ์ ๋ณต์กํ ๊ธฐ๋ฅ๋ค์ ์ง์ ๋ฐ์ ์ ์์ต๋๋ค.
๋ฐ๋ผ์ Qclass + MongoDB ๋ฐฉ์์ ํด๋จผ ์๋ฌ๋ก ์ธํ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐฉ์งํ๊ณ ์ถ์ ๋ถ๋ค์๊ฒ ์ถ์ฒ๋๋ฆฝ๋๋ค.
'DB > Mongo' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Mongo] ObjectId ์ค๋ณต๊ฐ๋ฅ์ฑ (2) | 2024.12.06 |
---|---|
[Mongo] MongoDB ์ค์น ๋ฐ ๊ฐ๋จํ ํ ์คํธ ์งํ (+์๋ฌํด๊ฒฐ) (0) | 2024.04.09 |