QueryDSL

정명진·2022년 11월 3일
0

QueryDSL 세팅 과정을 적어봅니다..

우선 gradle 설정을 아래와 같이 해줍니다.

buildscript {
   ext {
      queryDslVersion = "5.0.0"
   }
}

plugins {
   id 'org.springframework.boot' version '2.6.0'
   id 'io.spring.dependency-management' version '1.0.11.RELEASE'
   //querydsl 추가
   id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
   id 'java'
}

group = 'study'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
   compileOnly {
      extendsFrom annotationProcessor
   }
}

repositories {
   mavenCentral()
}

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
   implementation 'org.springframework.boot:spring-boot-starter-web'
   compileOnly 'org.projectlombok:lombok'
   runtimeOnly 'com.h2database:h2'
   //querydsl 추가
   implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
   implementation "com.querydsl:querydsl-apt:${queryDslVersion}"


   annotationProcessor 'org.projectlombok:lombok'
   testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
   useJUnitPlatform()
}

//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
   jpa = true
   querydslSourcesDir = querydslDir
}
sourceSets {
   main.java.srcDir querydslDir
}
compileQuerydsl{
   options.annotationProcessorPath = configurations.querydsl
}
configurations {
   compileOnly {
      extendsFrom annotationProcessor
   }
   querydsl.extendsFrom compileClasspath
}

그리고 나서 queryDSLcompile을 해줍니다. 여기서 될 수도 있고 안될수도 있음..

본인은 duplicate class 에러가 떴음.

찾아본 해결법은 2가지가 있었다.

  1. Intellij 세팅 변경
  2. gradle에 내용 추가

결과적으로 2개 다 시도했지만 1번은 실패하고 2번만 성공했다.

task generateQueryDSL(type: JavaCompile, group: 'build') {
    source = sourceSets.main.java
    classpath = configurations.compile
    options.compilerArgs = [
            "-proc:only",
            "-processor", "com.querydsl.apt.jpa.JPAAnnotationProcessor"
    ]
    destinationDir = file('./src/main/generated')
}

해당 내용 추가후 다시 compile 하면 이제 duplicate class가 뜨지 않음!

이제 queryDSL 세팅은 다 끝났고 사용만 하면 된다~

@Entity가 붙은 애들을 compile 할때 Q+${entityName}으로 클래스를 만들어준다.

만약 User 라는 Entity가 있다면 QUser 이렇게 만들어준다.

기존 프로젝트에서 본인은 ReviewRepository를 사용중이었다.

Spring Data JPA repository는 Custom Repository 추가가 가능하다.

ReviewRepository가 기존에 사용하던 repository고 Custom이 붙은게 QueryDSL 사용을 위해 만든 것들이다. 여기서 중요한점은 Impl을 붙여야 애가 인식을 해서 작동한다는것.

아래는 CustomRepository interface

package com.shinhan.review.repository;

import com.shinhan.review.entity.dto.ReviewDto;
import com.shinhan.review.entity.dto.ReviewExcelDto;
import com.shinhan.review.search.form.SearchForm;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;

public interface ReviewCustomRepository {
    Page<ReviewDto> findAllBySearchForm(SearchForm searchForm, Pageable pageable);
    List<ReviewExcelDto> getExcelBySearchForm(SearchForm searchForm);
}

해당 인터페이스를 구현한 class

package com.shinhan.review.repository;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.shinhan.review.entity.dto.QReviewDto;
import com.shinhan.review.entity.dto.QReviewExcelDto;
import com.shinhan.review.entity.dto.ReviewDto;
import com.shinhan.review.entity.dto.ReviewExcelDto;
import com.shinhan.review.search.form.SearchForm;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import java.util.List;

import static com.shinhan.review.entity.QReview.review;


@Repository
public class ReviewCustomRepositoryImpl implements ReviewCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;

    public ReviewCustomRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
        this.jpaQueryFactory = jpaQueryFactory;
    }

    @Override
    public Page<ReviewDto> findAllBySearchForm(SearchForm searchForm, Pageable pageable) {
        BooleanBuilder builder = getBooleanBuilder(searchForm);

        List<ReviewDto> result = jpaQueryFactory.select(new QReviewDto(review)).from(review).where(builder).offset(pageable.getOffset())
                .limit(pageable.getPageSize()).fetch();
        // list to page
        // need Count
        Long count = jpaQueryFactory.select(review.count()).from(review).where(builder).fetchOne();

        return new PageImpl<>(result, pageable, count);
    }

    /**
     * 조건문
     * @param searchForm
     * @return
     */
    private BooleanBuilder getBooleanBuilder(SearchForm searchForm) {
        BooleanBuilder builder = new BooleanBuilder();

        if (StringUtils.hasText(searchForm.getAppPkg())) {
            builder.and(review.appPkg.eq(searchForm.getAppPkg()));
        }
        if (searchForm.getOs()!=null) {
            builder.and(review.osType.eq(searchForm.getOs().getNumber()));
        }
        if (searchForm.getStart()!=null){
            builder.and(review.createdDate.goe(searchForm.getStrStart()));
        }
        if (searchForm.getEnd()!=null){
            builder.and(review.createdDate.lt(searchForm.getStrEnd()));
        }
        return builder;
    }

    @Override
    public List<ReviewExcelDto> getExcelBySearchForm(SearchForm searchForm) {
        BooleanBuilder builder = getBooleanBuilder(searchForm);
        return jpaQueryFactory.select(new QReviewExcelDto(review)).from(review).where(builder).fetch();
    }
}

QueryDSL을 사용하면 동적 쿼리 처리가 너무 편해진다!! 그리고 컴파일시 에러를 잡을 수 있다는 장점이 있다. DTO로 변환해서 결과를 return 할때 일일이 멤버 변수 세팅이 귀찮다면 @QueryProjection 어노테이션을 DTO에 붙여주면 DTO도 Q+{dtoName} 이렇게 생성이 가능하다. 하지만 DTO는 다방면에 사용할 가능성이 높은데... 해당 어노테이션을 사용하면 의존성이 높아진다는 단점이 생기게 된다. 이는 사용자의 몫이므로 편한대로 사용하면 될듯 합니다~

profile
개발자로 입사했지만 정체성을 잃어가는중... 다시 준비 시작이다..

0개의 댓글