Query DSL Query Domain Specific Language의 약자로 도메인 + 특화 + 언어라는 개념으로, 오픈소스 프로젝트이며 JPQL을 JAVA코드로 작성할 수 있게 해준다. 쉽게 말하면 기존의 쿼리처럼 sql문법에 맞추어 작성하지 않고 jAVA코드로만 작성할수 있도록 도와주는 라이브러리 이다.
Qudry dsl 을 이용해서 구현한 검색 기능인데 기억하기보다는 기록해 두고 싶었다
package com.ll.edubridge.domain.post.post.repository;
import com.ll.edubridge.domain.course.course.entity.QCourse;
import com.ll.edubridge.domain.member.member.entity.Member;
import com.ll.edubridge.domain.post.post.entity.Post;
import com.ll.edubridge.domain.post.post.entity.QPost;
import com.ll.edubridge.standard.base.KwTypeV1;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.support.PageableExecutionUtils;
import java.util.List;
import static com.ll.edubridge.domain.course.course.entity.QCourse.course;
import static com.ll.edubridge.domain.post.post.entity.QPost.*;
@RequiredArgsConstructor
public class CustomPostRepositoryImpl implements CustomPostRepository{
private final JPAQueryFactory queryFactory;
@Override
public Page<Post> findByKw(KwTypeV1 kwType, String kw, Member author, Boolean published, Pageable pageable) {
BooleanBuilder builder = new BooleanBuilder();
if (author != null) {
builder.and(post.writer.eq(author));
}
if (published != null) {
builder.and(post.published.eq(published));
}
if (kw != null && !kw.isBlank()) {
applyKeywordFilter(kwType, kw, builder);
}
JPAQuery<Post> postsQuery = createPostsQuery(builder);
applySorting(pageable, postsQuery);
postsQuery.offset(pageable.getOffset()).limit(pageable.getPageSize());
JPAQuery<Long> totalQuery = createTotalQuery(builder);
return PageableExecutionUtils.getPage(postsQuery.fetch(), pageable, totalQuery::fetchOne);
}
private void applyKeywordFilter(KwTypeV1 kwType, String kw, BooleanBuilder builder) {
switch (kwType) {
case kwType.TITLE -> builder.and(post.title.containsIgnoreCase(kw));
case kwType.BODY -> builder.and(post.content.containsIgnoreCase(kw));
case kwType.NAME -> builder.and(post.writer.nickname.containsIgnoreCase(kw));
default -> builder.and(
post.title.containsIgnoreCase(kw)
.or(post.content.containsIgnoreCase(kw))
.or(post.writer.nickname.containsIgnoreCase(kw))
);
}
}
private JPAQuery<Post> createPostsQuery(BooleanBuilder builder) {
return queryFactory
.select(post)
.from(post)
.where(builder);
}
private void applySorting(Pageable pageable, JPAQuery<Post> postsQuery) {
for (Sort.Order o : pageable.getSort()) {
PathBuilder pathBuilder = new PathBuilder(post.getType(), post.getMetadata());
postsQuery.orderBy(new OrderSpecifier(o.isAscending() ? Order.ASC : Order.DESC, pathBuilder.get(o.getProperty())));
}
}
private JPAQuery<Long> createTotalQuery(BooleanBuilder builder) {
return queryFactory
.select(post.count())
.from(post)
.where(builder);
}
}
이 코드는 Java로 작성된 CustomPostRepositoryImpl라는 클래스이다. 이 클래스는 CustomPostRepository 인터페이스를 구현하고 있고, JPAQueryFactory를 사용하여 데이터베이스로부터 Post 객체를 검색하는 메서드들을 제공한다.
findByKw 메서드:
Post 객체의 페이지를 반환한다.KwTypeV1 kwType, String kw, Member author, Boolean published, Pageable pageable이 있다.BooleanBuilder를 사용하여 조건부 쿼리를 구성합니다.applyKeywordFilter 메서드를 통해 처리되며, KwTypeV1에 따라 제목, 본문, 작성자 이름 중에서 검색할 수 있다.Pageable 객체를 사용하여 적용된다.applyKeywordFilter 메서드:
KwTypeV1에 따라 쿼리에 조건을 추가한다.KwTypeV1에 따라 제목, 본문, 작성자의 닉네임 필드에서 키워드를 검색할 수 있다.createPostsQuery 및 createTotalQuery 메서드:
createPostsQuery는 주어진 조건(BooleanBuilder)을 사용하여 Post 객체를 검색하는 JPAQuery를 생성한다.createTotalQuery는 총 결과 수를 계산하는 쿼리를 생성한다.applySorting 메서드:
Pageable 객체에서 정렬 조건을 추출하여 쿼리에 적용한다.JPAQueryFactory:
queryFactory 필드는 JPA 쿼리를 동적으로 생성하는 데 사용된다.이 코드를 작성하면서 BooleanBuilder에 대해 더 공부해 봤다.
BooleanBuilder는 Querydsl 라이브러리의 일부로, 동적으로 JPQL (Java Persistence Query Language) 또는 SQL 쿼리의 WHERE 조건을 구성하는 데 사용된다. 이 클래스는 복잡한 조건을 논리적으로 조합할 때 유용하다. 여기에는 기본적인 and, or, not 같은 논리 연산자를 사용할 수 있다.
조건의 동적 구성:
BooleanBuilder는 쿼리의 조건을 조건부로 구성하는 데 사용된다. 예를 들어, 사용자 입력에 따라 다른 검색 조건을 적용해야 할 때 유용하다.체이닝 방식:
BooleanBuilder는 메서드 체이닝을 지원한다. 즉, .and(), .or() 같은 메서드를 연속해서 호출하여 여러 조건을 쉽게 결합할 수 있다.Predicate 인터페이스:
BooleanBuilder는 Predicate 인터페이스를 구현한다. 따라서 Querydsl의 다른 API와 쉽게 결합하여 사용할 수 있다.예제 사용:
BooleanBuilder builder = new BooleanBuilder();
if (someCondition) {
builder.and(someEntity.someField.eq(someValue));
}
if (anotherCondition) {
builder.or(anotherEntity.anotherField.eq(anotherValue));
}
예를 들어, 사용자가 여러 필터 옵션을 선택할 수 있는 검색 시스템에서 BooleanBuilder를 사용하여 쿼리를 구성할 수 있다. 사용자가 선택한 각 옵션에 따라 .and() 또는 .or()를 사용하여 조건을 추가하고, 최종적으로 구성된 BooleanBuilder 객체를 쿼리의 WHERE 절에 적용한다.
BooleanBuilder는 초기 상태가 null과 같다. 즉, 아무런 조건도 추가되지 않으면, 이는 "모든 조건에 대해 참"으로 인식한다.