QueryDSL이 필요한 이유
현재 모든 Database에 SQL 쿼리를 JPA를 활용하여 쉽게 결과를 받아왔다.
JPA의 경우
검색 키워드들을 기반으로 검색결과를 반환하는 시스템을 구축하며 겪은 문제
QueryDSL의 장점
Configurations
// QueryDSL Version 5부터 추가가 필요하다고 한다.
// 최상단에 명시
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
...
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" // 작성
...
}
...
// QueryDSL 설정 start
def querydslDir = "$buildDir/generated/querydsl"
dependencies {
...
// [QueryDSL]
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
}
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
Why QClass ?
Querydsl을 이용하려면 기존에 작성한 Entity 클래스를 그대로 이용하는 것이 아닌 Q도메인 클래스를 이용해야만 한다.
Querydsl의 핵심 원칙은 타입 안정성(Type Safety)이다. 도메인 타입의 프로퍼티를 반영해 생성한 쿼리 타입을 이용해 쿼리를 작성하게 된다. 또한, 완전하게 타입에 안전한 방법으로 함수 or 메서드 호출이 이뤄진다.
Gradle -> Tasks -> other -> compileQuerydsl 실행
내가 작성한 Entity들에 대하여 /build/generated에 QClass 생성
사용해보기 (예제)
Spring Data Jpa에 포함된 클래스로 QueryDsl 라이브러리를 통해 직접 구현할 경우 필요
생성자가 존재하므로 super()을 이용해 호출하고 super(Domain.class)에 null이 올 수 없다.
Domain : Post.class
@Repository
public class PostRepositorySupport extends QuerydslRepositorySupport {
private final JPAQueryFactory jpaQueryFactory;
private final EntityManager entityManager;
public PostRepositorySupport(JPAQueryFactory jpaQueryFactory, EntityManager entityManager) {
super(Post.class);
this.jpaQueryFactory = jpaQueryFactory;
this.entityManager = entityManager;
}
}
from : 쿼리 소스를 추가한다.
innerJoin, join, leftJoin, fullJoin, on : 조인 부분을 추가한다. 조인 메서드에서 첫 번째 인자는 조인 소스이고, 두 번째 인자는 대상(별칭)이다.
where : 쿼리 필터를 추가한다. 가변 인자나 AND/OR 메서드를 이용해 필터를 추가한다.
groupBy : 가변인자 형식의 인자를 기준으로 그룹을 추가한다.
having : Predicate 표현식을 이용해 "group by" 그룹핑의 필터를 추가한다.
orderBy : 정렬 표현식을 이용해서 정렬 순서를 지정한다. 숫자나 문자열에 대해서는 asc()나 desc()를 사용하고, OrderSpecifier에 접근하기 위해 다른 비교 표현식을 사용한다.
limit, offset, restrict : 결과의 페이징을 설정한다. limit은 최대 결과 개수, offset은 결과의 시작 행, restrict는 limit과 offset을 함께 정의한다.
queryDSL ref에 대한 더 자세한 내용 : http://querydsl.com/static/querydsl/4.0.1/reference/ko-KR/html_single/
public PageImpl<Post> findCustomSearchResultsWithPagination(String sentence, Pageable pageable) {
ArrayList<String> phrase_list = new ArrayList<>();
ArrayList<String> tag_list = new ArrayList<>();
ArrayList<String> exact_phrase_list = new ArrayList<>();
BooleanBuilder searchBuilder = new BooleanBuilder();
QPost post = QPost.post;
... // 검색 문장 처리 로직 ( 길어서 생략 )
// 태그
for( String tag : tag_list ){
searchBuilder.or(QPost.post.tags.contains(tag)); // OR Post.tags LIKE %:tag%
}
for( String exact_phrase : exact_phrase_list ){
searchBuilder.or(QPost.post.title.contains(exact_phrase));
searchBuilder.or(QPost.post.content.contains(exact_phrase));
}
for( String phrase : phrase_list ){
searchBuilder.or(QPost.post.title.contains(phrase));
searchBuilder.or(QPost.post.content.contains(phrase));
}
JPAQuery<Post> postJPAQuery = jpaQueryFactory.selectFrom(QPost.post)
.where(searchBuilder)
.orderBy(QPost.post.createdTime.desc());
long totalCount = postJPAQuery.stream().count();
List<Post> results = getQuerydsl().applyPagination(pageable, postJPAQuery).fetch();
return new PageImpl<>(results, pageable, totalCount);
}
참고:
https://velog.io/@solchan/Spring-QueryDSL-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0
https://ict-nroo.tistory.com/117
https://dev-gorany.tistory.com/32
https://jessyt.tistory.com/55