Runjob(런잡 프로젝트)/SpringBoot + Kotlin

[SpringBoot + Kotlin] Querydsl 적용 및 DB 관리

맏리믓 2023. 4. 26. 00:48

들어가며

- 기존에 사용 했었던 JPA 의 기본적인 메소드인 find, save 등 만으론 원하는 모든 기능을 구현 하기는 힘들다.

- Querydsl 을 사용하면 이러한 점을 보안 할 수 있다.

- SQL 의 쿼리문과 유사 하기 때문에 구현 하고자 하는 대부분의 기능을 구형 가능 하다.


Build.gradle 파일 수정

- querydsl 을 사용하기 위해 필요한 플러그인과 설정들이 필요하다.

- 다음과 같이 설정 하면 된다.

- 설정 후 run 을 해 준다.(dependencies 에 kapt 추가시 에러가 날 수 있는데 그럼 해당 코드를 빼고 run 을 한 후 추가 하면 된다.)

//biild.gradle.kt

plugins {
   id("org.springframework.boot") version "3.0.5"
   id("io.spring.dependency-management") version "1.1.0"
   id("org.graalvm.buildtools.native") version "0.9.20"
   kotlin("jvm") version "1.7.22"
   kotlin("plugin.spring") version "1.7.22"
   kotlin("plugin.jpa") version "1.7.22"
   
   kotlin("kapt") version "1.7.22"//kapt 플러그인 추가를 위해 코드 추가
   
}
dependencies {
   implementation("org.springframework.boot:spring-boot-starter-data-jpa")
   implementation("org.springframework.boot:spring-boot-starter-mustache")
   implementation("org.springframework.boot:spring-boot-starter-web")
   implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
   implementation("org.json:json:20230227")
   implementation("org.jsoup:jsoup:1.15.4")
   implementation("org.jetbrains.kotlin:kotlin-reflect")
   
   implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta") // querydsl dependencies 추가
   
   developmentOnly("org.springframework.boot:spring-boot-devtools")
   runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
   annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
   annotationProcessor("org.projectlombok:lombok")
   testImplementation("org.springframework.boot:spring-boot-starter-test")
   
   kapt("com.querydsl:querydsl-apt:5.0.0:jakarta") // kapt 설정 추가
}

Qclass 추가

- 위 설정 대로 하고 build.gradle 을 run 하게 되면 자신이 추가 해 둔 Entity 의 Qclass 가 생기게 된다.

- 이름이 test 인 Entity 의 Qclass "Qtest" 가 생성 되었음을 볼 수 있다.


DTO 파일 추가

- queryProjection 을 위한 DTO 파일을 생성 한다.

- 이 파일을 통해 query 문 전닿후 데이터를 받는다.

- Entity 디렉토리 내에 DTO 디렉토리를 생성 후 DTO 파일을 생성 해 준다.

- 내부 구조는 앞서 만들었던 test.kt 데이터 class 와 동일하게 생성 해 준다.

//testDTO.kt

package com.example.runjob_blog.Entity.DTO

import com.querydsl.core.annotations.QueryProjection

data class testDTO @QueryProjection constructor(
    val testId: Int = 0,

    val testName: String = "",

    val testInfo: String = "",
)

QueryDslConfig

- 프로젝트 어디서든지 QueryFactory 를 import 해서 사용하기 위해 QueryFactory 를 Bean 으로 등록한다.

- 새로이 config 디렉토리를 생성후 해당디렉토리 내에 config 파일을 생성 한다.

- 파일에 다음과 같은 내용을 적어 저장 한다.

//QueryDslConfig.kt

package com.example.runjob_blog.config

import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class QueryDslConfig{

      @PersistenceContext
      lateinit var entityManager: EntityManager

      @Bean
      fun jpaQueryFactory(): JPAQueryFactory {
         return JPAQueryFactory(entityManager)
      }
}

Querydsl 실제로 사용

- 사용을 위한 파일(testRepositorySupport.kt) 를 만들어 준다.

- 실제로 Querydsl 을 사용하여 DB 중 ID 가 1인 데이터를 불러 보겠다.

- 다음 코드처럼 SQL 명령어와 유사한 모습을 볼 수 있다.

- 코드는 "test" Table 에서 testId 가 "1" 인 데이터의 Id, Info, Name 을 불러 오는 코드이다.

//testRepositorySupport.kt

package com.example.runjob_blog.Repository.Support

import com.example.runjob_blog.Entity.DTO.testDTO
import com.example.runjob_blog.Entity.Qtest
import com.example.runjob_blog.Entity.test
import com.querydsl.core.types.Projections
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport
import org.springframework.stereotype.Repository

@Repository
class testRepositorySupport(
    val query: JPAQueryFactory
): QuerydslRepositorySupport(test::class.java){
    val test: Qtest = Qtest.test

    fun getTest(): List<testDTO>{
        return query
            .select(
                Projections.fields(
                    testDTO::class.java,
                    test.testId,
                    test.testInfo,
                    test.testName
                )
            )
            .from(test)
            .where(test.testId.eq(1))
            .fetch()
    }
}

결과

- 앞의 글들과 마찬가지로 controller 에 하나를 추가 해 주고 실행 해 본다.

//BlogTestController.kt

package com.example.runjob_blog.Controller

import com.example.runjob_blog.Repository.Support.testRepositorySupport
import com.example.runjob_blog.Service.movieInfoService
import com.example.runjob_blog.Service.testService
import com.example.runjob_blog.Utils.request_API
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime

@RestController
@RequestMapping("/test")
class BlogTestController(
    val testService: testService,
    val movieInfoService: movieInfoService,
    val testRepositorySupport: testRepositorySupport
) {
//							.
//							.
//						      중 략
//							.
//							.

    @GetMapping("querydsl")
    fun Querydsl_test(): String{
        return testRepositorySupport.getTest().toString()
    }
}

- 아래 DB 에서 원하는 값을 잘 가져 오는 것을 알 수 있다.