QueryDSL

QueryDSL의 장점 및 gradle-groovy의 적용 방법은 이 글에 정리해두었다.

위 글에서는 gradle-groovy 에서의 세팅 방법이었다.

이번 글에서는 gradle-kotlin 에서 세팅하는 방법을 정리해두려 한다.


gradle-kotlin에서 QueryDSL 적용

gradle-groovy 와 gradle-kotlin 은 문법이 많이 달라서 바꿔서 적용하느라 고생을 꽤 했다.

그래도 한번 정리해두면 두고두고 쓸 수 있을것 같다.

build.gradle.kts 

build.gradle.kts의 전문은 아래와 같다.

plugins {
   java
   id("org.springframework.boot") version "3.3.0"
   id("io.spring.dependency-management") version "1.1.5"
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
val queryDslVersion = "5.0.0" // QueryDSL Version 설정

java {
   toolchain {
      languageVersion = JavaLanguageVersion.of(17)
   }
}

configurations {
   compileOnly {
      extendsFrom(configurations.annotationProcessor.get())
   }
}

repositories {
   mavenCentral()
}

dependencies {

  ...

   // QueryDSL 의존성 추가
   implementation("com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta")
   implementation ("com.querydsl:querydsl-core")
   implementation ("com.querydsl:querydsl-collections")
   annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jakarta")
   annotationProcessor("jakarta.annotation:jakarta.annotation-api")
   annotationProcessor("jakarta.persistence:jakarta.persistence-api")
   
   ...
}

tasks.withType<Test> {
   useJUnitPlatform()
}

``` 설정 추가 시작
val querydslDir = layout.buildDirectory.dir("generated/querydsl").get().asFile

sourceSets.getByName("main") {
   java.srcDir(querydslDir)
}

tasks.withType<JavaCompile> {
   options.generatedSourceOutputDirectory.set(file(querydslDir))
}

// clean 이후에 querydsl 폴더를 지움
tasks.named("clean") {
   doLast {
      file(querydslDir).delete()
   }
}
``` 설정 추가 끝

'백엔드 > 연습' 카테고리의 다른 글

Spring Boot Actuator 써보기  (0) 2024.06.05
EasyRandom 라이브러리 사용해보기  (0) 2024.05.23
포트원 API 사용해보기  (0) 2024.04.04
plain-jar 파일 생성 방지  (0) 2024.01.16
JWT 발급해보기  (0) 2023.10.04

문제 발견

기존 QueryDsl 없이 NativeQuery 로 작성한 리포지토리 메서드를 리팩토링 하다가 발생한 에러이다.

@SpringBootTest 는 스프링을 띄워서 의존성 주입을 다 받기 때문에 상관없지만

리포지토리 테스트는 대부분 @DataJpaTest 를 사용하기 때문에 QueryDsl 관련 빈을 주입받지 못해 발생한 에러였다.

 


문제 해결

아래 두가지를 순서대로 따라하니 문제가 해결되었다.

 

1. QueryDslConfig 작성

QueryDslConfig

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    public EntityManager em;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

 

2. Test 에 @Import 추가

Test

@DataJpaTest
@Import(QueryDslConfig.class)
class ProductRepositoryTest {
...
}

 

 

참고자료)

 

No qualifying bean of type 'com.querydsl.jpa.impl.JPAQueryFactory' available: expected at least 1 bean which qualifies as autowi

JPA Config 파일 추가test에 @Import 추가

velog.io

 

 

@DataJpaTest - 인프런 | 질문 & 답변

QueryDsl 강의 잘듣고 있습니다!QueryDsl을 사용하면 @DataJpaTest를 사용하지 못하나요?JPAQueryFactory에 대해서 NoSuchBeanDefinitionException이 발생하는데 따로 주입하는 방법을 모르겠습니다ㅠㅠ - 질문 & 답변

www.inflearn.com

 

문제 발견

이 글처럼 설정 후 Q클래스가 생성된건 확인했지만 아래 사진처럼 Q클래스를 import 할 수 없었다.

 

 

generated 폴더에 Q클래스 파일이 제대로 추가되지 않아 발생한 문제였다.

 


문제 해결

기존 generated 폴더를 삭제하고 애플리케이션을 재실행하면 generated 폴더가 재생성 되면서 Q클래스 파일이 추가된다.

 

 

끝 !

QueryDSL 이란

QueryDsl은 하이버네이트 쿼리 언어(HQL: Hibernate Query Language)의 쿼리를 타입에 안전하게 

생성 및 관리해주는 프레임워크이다.

QueryDsl 과 SpringDataJPA 를 함께 사용하여 기존 JPA 문제점인 복잡한 쿼리 및 동적 쿼리 작성의 한계를 보완할 수 있다.

[ QueryDsl의 장점 ]

  1. 쿼리를 자바 코드로 작성하므로 문법 오류를 컴파일 시점에 잡아낼 수 있다.
  2. 복잡한 쿼리나 동적 쿼리를 편리하게 작성할 수 있다.
  3. 인텔리제이와 같은 IDE의 자동 완성 기능의 도움을 받을 수 있다.

아래의 두 코드는 동일하게 작동하는 코드이다.

위는 NativeQuery 인데 문자열로 쿼리를 작성하기 때문에 실수의 여지가 많다.

아래처럼 QueryDsl 사용 시 자바 코드로 쿼리를 작성하므로 컴파일 시점에서 문법 오류를 잡아낼 수 있다.

@Query("select o from Order o where o.registeredDateTime >= :startDateTime" +
        " and o.registeredDateTime < :endDateTime" +
        " and o.orderStatus = :orderStatus")
List<Order> findOrdersBy(@Param("startDateTime") LocalDateTime startDateTime,
                         @Param("endDateTime") LocalDateTime endDateTime,
                         @Param("orderStatus") OrderStatus orderStatus);
@Override
public List<Order> findOrdersBy(LocalDateTime startDateTime, 
                            LocalDateTime endDateTime, 
                            OrderStatus orderStatus) {
    return jpaQueryFactory
        .selectFrom(order)
        .where(order.registeredDateTime.between(startDateTime, endDateTime.minusNanos(1)),
                order.orderStatus.eq(orderStatus))
        .fetch();
}

 


QueryDsl 의존성 추가

1. gradle 의 plugin 사용 시

전엔 Querydsl 라이브러리를 사용하려면 아래처럼 이것저것 복잡한 설정을 했어야했다.

구닥다리 방법이니 대충 눈으로만 보고 넘어가도 좋다.

 

build.gradle

plugins {
   id 'java'
   id 'org.springframework.boot' version '2.7.8'
   id 'io.spring.dependency-management' version '1.0.15.RELEASE'
   //querydsl 추가
   id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
apply plugin: "com.ewerk.gradle.plugins.querydsl"
//querydsl 추가
implementation 'com.querydsl:querydsl-jpa'
//querydsl 추가
implementation 'com.querydsl:querydsl-apt'
//querydsl 추가 시작
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
   library = "com.querydsl:querydsl-apt"
   jpa = true
   querydslSourcesDir = querydslDir
}
sourceSets {
   main.java.srcDir querydslDir
}
configurations {
   querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
   options.annotationProcessorPath = configurations.querydsl
}
//querydsl 추가 끝

 

2. gradle 의 Annotation processor 와 Querydsl

간단히 요약하자면 기존엔 Querydsl 라이브러리를 쓰기 위해서는 gradle 의 plugin 을 써야했지만

이젠 annotationProcessor 을 사용하여 쉽고 편리하게 의존성을 추가한다.

자세한건 허니몬님 블로그를 참고하고 코드로 넘어가겠다.

 

build.gradle

// querydsl
implementation "com.querydsl:querydsl-core"
implementation "com.querydsl:querydsl-jpa"

// querydsl JPAAnnotationProcessor 사용 지정
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa" 
// java.lang.NoClassDefFoundError(javax.annotation.Entity) 발생 대응
annotationProcessor "jakarta.persistence:jakarta.persistence-api" 
// java.lang.NoClassDefFoundError (javax.annotation.Generated) 발생 대응
annotationProcessor "jakarta.annotation:jakarta.annotation-api"

 

이렇게만 추가하고 코끼리를 눌러서 gradle 을 최신화 해준다.

이후 아래의 순서대로 눌러준다.

 

 

그리고 generated 의 annotationProcessor 폴더를 확인해서 Q 클래스 생성을 확인하면 성공이다.

 

추가로 main 의 generated 폴더에도 제대로 생성이 되었는지 확인한다.

이렇게하여 gradle 의존성 추가 및 Q 클래스 생성 확인까지 마쳤다.

만약 main 의 generated 폴더에 Q 클래스 생성이 안된다면 여기를 참고하자.

 


사용자 정의 리포지토리 설정

지금까지 QueryDsl 을 사용할 기본준비를 마쳤다.

하지만 JPA 와 함께 사용하기 위해서는 사용자 정의 리포지토리(CustomRepository) 설정이 필요하다.

 

[ 사용자 정의 리포지토리 사용법 ]

  1. QueryDslConfig 작성
  2. 사용자 정의 인터페이스 작성
  3.  사용자 정의 인터페이스 구현체 생성
  4.  스프링 데이터 리포지토리에 사용자 정의 인터페이스 상속

그림으로 표현하면 아래와 같다.

 

1. QueryDslConfig

가장 먼저 QueryDsl 을 JPA 와 함께 사용하기 위해 필요한 구현체인 JPAQueryFactory 의 빈 등록을 위한 Config 설정이다.

@Configuration
public class QueryDslConfig {

    @PersistenceContext
    public EntityManager em;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(em);
    }
}

 

2. 인터페이스 PostRepositoryCustom 

사용자 정의 리포지토리를 설정해야한다.

public interface PostRepositoryCustom {

    List<Post> getList(PostSearch postSearch);
}

 

3. 구현체 PostRepositoryImpl

@Repository
@RequiredArgsConstructor
public class PostRepositoryImpl implements PostRepositoryCustom{

    private final JPAQueryFactory jpaQueryFactory;

    @Override
    public List<Post> getList(PostSearch postSearch) {
        return jpaQueryFactory.selectFrom(QPost.post)
            .limit(postSearch.getSize())
            .offset(postSearch.getOffset())
            .orderBy(QPost.post.id.desc())
            .fetch();
    }
}

 

3. PostRepository 에 PostRepositoryCustom 상속

public interface PostRepository extends JpaRepository<Post, Long>, PostRepositoryCustom {
}

 

 

출처)

허니몬님 블로그

 

[gradle] 그레이들 Annotation processor 와 Querydsl - I'm honeymon(JiHeon Kim).

이 글에서 다룰 예정인 ‘Querydsl’과 ‘Annotation processor’ 에 관한 내용도, 스프링 부트를 버전업하는 과정에서 겪게 된다. 사내 개발기기 교체주기가 되어 새로운 맥북을 받고 스프링 부트 버전

honeymon.io

 

김영한님 QueryDsl 강의

 

 

실전! Querydsl - 인프런 | 강의

Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, 복잡한 쿼리, 동적 쿼리는 이제 안녕! Querydsl로 자바 백엔드 기술을 단단하게. 🚩 본 강의는 로드맵 과정입니다. 본 강의는 자바 백엔

www.inflearn.com

 

'백엔드 > 연습' 카테고리의 다른 글

QueryDSL 적용하기: gradle-kotlin  (0) 2024.05.13
포트원 API 사용해보기  (0) 2024.04.04
plain-jar 파일 생성 방지  (0) 2024.01.16
JWT 발급해보기  (0) 2023.10.04
H2 DB 사용해보기  (0) 2023.09.24

+ Recent posts