본문 바로가기

Spring

[Spring]QueryDSL 적용하기

Query DSL이란?

Spring에서 JPA는 기본적으로 제공하는 CRUD 메서드나 쿼리 메서드 기능이 있는데, 원하는 조건을 만들기 위해서는 복잡하게 로직을 만들 수 있어도, 쿼리 문자열이 상당히 길어진다.

또한 원하는 조건을 얻기 위해 JPQL을 작성하게 되는데, JPQL 문자열에 오타 혹은 문법적 오류 발생 시 로딩에서는 발견할 수 있으나, 그 외에서는 런타임 시점에서 에러가 발생된다.

 

이 문제들을 해결하기 위해 QueryDSL이 만들어졌다.

이는 정적타입을 이용해 SQL 등 쿼리를 생성해주는 오픈소스 프레임 워크이며

 1. 문자가 아닌 JAVA 코드로 쿼리를 작성하여 컴파일 시점에서 문법 오류를 발견 가능.

 2. 동적 쿼리 작성

 3. 자동 완성 기능 사용 가능

등 장점이 존재한다. 하지만, QueryDSL을 사용하기 위해 적용하는 방법이 어렵다.

 

Query DSL vs 일반 JPA 함수

장점 복잡한 쿼리를 만들 수 있다. 사용법이 단순하다.
단점 코딩을 더 많이 해야 한다. 복잡한 쿼리를 만들 수 없다.

 

 

QueryDSL을 적용하는 법을 알아보자.

QueryDSL 적용

// build.gradle에 추가

// querydsl 추가
buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}

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

group = 'com.ll.exam'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

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'
  developmentOnly 'org.springframework.boot:spring-boot-devtools'
  runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
  annotationProcessor 'org.projectlombok:lombok'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
  
  //querydsl 추가
  implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
  annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
}

tasks.named('test') {
  useJUnitPlatform()
}

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

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

QueryDSL은 세부적인 쿼리문까지 작성할 수 있는데, 작성하기 위해서는 QueryRepository에 RepositoryCustom을 상속해주어야 한다.

public interface UserRepository extends JpaRepository<SiteUser, Long>, UserRepositoryCustom {
}

그리고 이 RepositoryCustom Interface로 추상 메서드만 구성할 수 있는데, 추상 메서드들을 구상하기 위해 구상 메서드를 만들 수 있는 RepositoryImpl 클래스를 생성해 RepositoryCustom을 implement 해준다.

@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepositoryCustom {
    private final JPAQueryFactory jpaQueryFactory;
}

여기서 JPAQueryFactory가 존재하는데, JpaQueryFactory를 통해 Java로 실제 쿼리문을 작성할 수 있도록 만든다.

해당 객체는 Bean으로 만들어주기 위해 base/AppConfig 클래스를 생성하여 Bean객체로 만들어준다.

@Configuration
public class AppConfig {
    @Bean
    public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
        return new JPAQueryFactory(entityManager);
    }
}

 

테스트

테스트를 QueryDSL을 사용해 findById 메서드를 만들어보자.

// UserRepositoryImpl.java

@Override
public SiteUser getQslUser(Long id) {
    return jpaQueryFactory
            .select(siteUser)
            .from(siteUser)
            .where(siteUser.id.eq(id))
            .fetchOne();
}

JpaQueryFactory로 쿼리문을 작성해 SiteUser객체를 만들어 냈다. 이 객체는 RepositoryCustom에

public interface UserRepositoryCustom {
    SiteUser getQslUser(Long id);
}

하나의 추상 메서드로 만들어 놓은 후 생성했으며 UserRepository에 상속되어 있기 때문에 getQslUser 메서드를 Repository를 통해 사용할 수 있다.

@Test
@DisplayName("1번 회원 찾기")
void t2() {
	SiteUser u1 = userRepository.getQslUser(1L);

	assertThat(u1.getId()).isEqualTo(1L);
	assertThat(u1.getUsername()).isEqualTo("user1");
	assertThat(u1.getPassword()).isEqualTo("{noop}1234");
	assertThat(u1.getEmail()).isEqualTo("user1@test.com");
}

 

 

'Spring' 카테고리의 다른 글

[JAVA] JAR과 WAR  (0) 2023.02.06
[Spring, QueryDSL]기본 테스트 데이터 생성  (0) 2022.08.29
[Spring]기능 - 검색  (0) 2022.08.25
[Spring]기능 - 앵커(anchor)  (0) 2022.08.23
[Spring]기능 - 추천 기능  (0) 2022.08.23