본문 바로가기
JPA/QueryDSL

QueryDSL 적용방법 (3가지)

by BAYABA 2021. 10. 12.
  1. 개인 공부 목적으로 작성한 글입니다.
  2. 아래 출처를 참고하여 작성하였습니다.

목차

  1. Intro
  2. Gradle 설정
  3. QueryDSL Configuration(Java Config)
  4. 테스트용 Entity, Repository
  5. QueryDSL 사용방법1. 기본 사용법
  6. QueryDSL 사용방법2. Spring Data Jpa Custom Repository 적용
  7. QueryDSL 사용방법3. 상속/구현 없는 Repository

1. Intro

  1. Spring Data JPA 프로젝트에 QueryDSL을 적용하는 세 가지 방법에 대해 알아보겠습니다.
  2. 여기서는 gradle을 사용합니다.

2. Gradle 설정

  1. querydsl-jpa : QueryDSL JPA 라이브러리입니다.
  2. querydsl-apt : 쿼리 타입(Q)를 생성할 때 사용하는 라이브러리입니다.
  • 쿼리 타입이란 엔티티를 기반으로 생성된 쿼리용 클래스를 말합니다.
//build.gradle

plugins {
    id 'org.springframework.boot' version '2.5.5'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

def querydslVersion = '4.1.3'

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'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

    implementation group: 'com.querydsl', name: 'querydsl-jpa', version: querydslVersion
    implementation group: 'com.querydsl', name: 'querydsl-core', version: querydslVersion
    implementation group: 'com.querydsl', name: 'querydsl-apt', version: querydslVersion

    annotationProcessor "com.querydsl:querydsl-apt:${querydslVersion}:jpa"
    annotationProcessor("jakarta.persistence:jakarta.persistence-api")
    annotationProcessor("jakarta.annotation:jakarta.annotation-api")
}

3. QueryDSL Configuration(Java Config)

  1. 아래 설정으로 프로젝트 어느 곳에서나 JPAQueryFactory를 주입받아 QueryDSL을 사용할 수 있게 됩니다.
package com.example.querydsltest.config;

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

@Configuration
public class QuerydslConfiguration {

  @PersistenceContext
  private EntityManager entityManager;

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

}

4. 테스트용 Entity, Repository

  1. 테스트로 사용할 Entity와 테스트 데이터를 넣고 검증할 Repository를 생성해놓겠습니다.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Academy {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;
  private String address;

  @Builder
  public Academy(String name, String address) {
    this.name = name;
    this.address = address;
  }
}

@Repository
public interface AcademyRepository extends JpaRepository<Academy, Long> {

}

5. QueryDSL 사용방법1. 기본 사용법

  1. QuerydslRepositorySupport를 상속받아 사용하는 방법입니다.
  2. 또한, 위에서 Config로 등록했던 jpaQueryFactory Bean을 생성자 인잭션으로 주입받아 사용합니다.
  3. 이 코드에서 중요한 건 빌드를 하여 생성된 Entity의 QClass를 사용합니다.
  • 해당 코드에서 option+enter를 사용해 Import를 진행하시면 됩니다.
  • 그럼 아래와 같이 Import가 된 것을 확인할 수 있습니다.
//...
import static com.example.querydsltest.domain.QAcademy.academy;

//...

@Repository
public class AcademyRepositorySupport extends QuerydslRepositorySupport {

  private final JPAQueryFactory queryFactory;

  public AcademyRepositorySupport(JPAQueryFactory queryFactory) {
    super(Academy.class);
    this.queryFactory = queryFactory;
  }

  public List<Academy> findByName(String name) {
    return queryFactory.selectFrom(academy)
        .where(academy.name.eq(name))
        .fetch();
  }
}

Test Code

  1. Querydsl이 정상작동했다면 findByName이라는 메소드가 정상작동할 것입니다.
  2. 아래와 같이 테스트 코드를 작성해서 검증해보겠습니다.
  • 코드는 간단합니다.
  • 1개의 Academy 데이터를 넣고, Querydsl로 만든 findByName메소드로 조회시 정상적으로 결과가 나오는지 확인합니다.
@ExtendWith(SpringExtension.class)
@SpringBootTest
class AcademyTest {

  @Autowired
  private AcademyRepository academyRepository;

  @Autowired
  private AcademyRepositorySupport academyRepositorySupport;

  @Test
  public void querydsl_기본_기능_확인() {
    //given
    String name = "jojoldu";
    String address = "jojoldu@gmail.com";
    academyRepository.save(new Academy(name, address));

    //when
    List<Academy> result = academyRepositorySupport.findByName(name);

    //then
    assertThat(result.size()).isEqualTo(1);
    assertThat(result.get(0).getAddress()).isEqualTo(address);
  }
}

6. QueryDSL 사용방법2. Spring Data Jpa Custom Repository 적용

  1. 위와 같은 방식으로도 QueryDSL을 사용할 수 있지만, 한 가지 단점이 있습니다.
  2. 항상 2개의 Repository를 의존성으로 받아야 한다는 점입니다.
  • 아래 두 개의 Repository가 기능을 나눠가져서 그렇습니다.
  • QueryDSL의 Custom Repository, JpaRepository의 Repository
  • 이를 해결하기 위해 Spring Data JPA에서는 Custom Repository를 JpaRepository 상속 클래스에서 사용할 수 있도록 기능을 지원합니다.
  • 전체적인 그림은 아래와 같습니다.


  1. 위와 같이 구성하면 AcademyRepository에서 AcademyRepositoryImpl의 코드도 사용할 수 있습니다.
  2. Custom이 붙은 인터페이스를 상속한 Impl 클래스의 코드는 Custom 인터페이스를 상속한 JpaRepository에서도 사용할 수 있습니다.
  • 일종의 공식이라고 보면 됩니다.
  • CustomImpl만 기억하면 됩니다.
  1. 코드로 진행해보겠습니다.
  • 먼저 AcademyRepository와 같은 위치에 AcademyRepositoryCustom 인터페이스와 AcademyRepositoryImpl 클래스를 생성합니다.

public interface AcademyRepositoryCustom {
  List<Academy> findByName(String name);
}

  1. Impl 클래스는 기존에 있던 Support 클래스 코드를 참고해서 구현하면 됩니다.
  2. Impl 클래스는 페이징이 필요한 게 아니라면 QuerydslSupport 상속 코드를 추가할 필요는 없습니다.
  • 결국 JPAQueryFactory를 통해서 작동하기 때문입니다.
import static com.example.querydsltest.domain.QAcademy.academy;

@RequiredArgsConstructor
public class AcademyRepositoryImpl implements AcademyRepositoryCustom {

  private final JPAQueryFactory queryFactory;

  @Override
  public List<Academy> findByName(String name) {
    return queryFactory.selectFrom(academy)
        .where(academy.name.eq(name))
        .fetch();
  }
}

  1. 그리고 이 코드를 AcademyRepository에서 쓸 수 있게 상속 구조로 변경합니다.
  • 이제 AcademyRepository가 정상 동작하는지 테스트 해보면 됩니다.
@Repository
public interface AcademyRepository extends JpaRepository<Academy, Long>, AcademyRepositoryCustom {

}

Test Code

@ExtendWith(SpringExtension.class)
@SpringBootTest
class AcademyTest {

  @Autowired
  private AcademyRepository academyRepository;

  @Test
  public void querydsl_기본_기능_확인() {
    //given
    String name = "jojoldu";
    String address = "jojoldu@gmail.com";
    academyRepository.save(new Academy(name, address));

    //when
    List<Academy> result = academyRepository.findByName(name);

    //then
    assertThat(result.size()).isEqualTo(1);
    assertThat(result.get(0).getAddress()).isEqualTo(address);
  }
}

7. QueryDSL 사용방법3. 상속/구현 없는 Repository

  1. 마지막 방식은 QueryDSL만으로 Repository를 구성하는 방법입니다.
  2. 아래처럼 JPAQueryFactory만 있으면 QueryDSL을 사용할 수 있습니다.
  • 최소한의 Bean 등록을 위해 @Repository를 선언합니다.
  • 별도의 extends / implements 없이 JPAQueryFactory만 있으면 됩니다.
  • 특정 Entity만 사용해야한다는 제약도 없습니다.
import static com.example.querydsltest.domain.QAcademy.academy;


@RequiredArgsConstructor
@Repository
public class AcademyQueryRepository {

  private final JPAQueryFactory queryFactory;

  public List<Academy> findByName(String name) {
    return queryFactory.selectFrom(academy)
                      .where(academy.name.eq(name))
                      .fetch();
  }

}

Test Code

@ExtendWith(SpringExtension.class)
@SpringBootTest
class AcademyTest {

  @Autowired
  private AcademyRepository academyRepository;

  @Autowired
  private AcademyQueryRepository academyQueryRepository;

  @Test
  public void querydsl_기본_기능_확인() {
    //given
    String name = "jojoldu";
    String address = "jojoldu@gmail.com";
    academyRepository.save(new Academy(name, address));

    //when
    List<Academy> result = academyQueryRepository.findByName(name);

    //then
    assertThat(result.size()).isEqualTo(1);
    assertThat(result.get(0).getAddress()).isEqualTo(address);
  }
}

출처

  1. Spring Boot Data Jpa 프로젝트에 Querydsl 적용하기
  2. [jpa] QueryDSL
  3. QueryDSL 입문!

'JPA > QueryDSL' 카테고리의 다른 글

about QueryDSL  (0) 2021.10.13