QueryDsl

이호영·2023년 3월 13일
0

QueryDsl

QueryDsl이란

Query Domain Specific Language의 약자로 오픈소스 프로젝트이며 JPQL을 JAVA코드로 작성할 수 있도록 해주는 라이브러리다.
즉, 기존 Query를 작성 시 SQL문법 규칙 대로 작성 해야하지만 그러지 않고 JAVA코드로만 작성할 수 있도록 도와주는 라이브러리로 생각하면 쉽다.

왜 QueryDsl을 사용할까?

JPA를 사용하면 QueryMethod와 @Query를 통해 여러 기능을 만들 수 있지만 이는 고정된 값을 가진다는 단점이 있다.
쉽게 말해, 쇼핑몰을 예로 들자면 카테고리가 없고 상품만 있는 쇼핑몰에서는 충분히 사용 가능 하지만 카테고리가 대분류 소분류로 나누어 지고 이러한 카테고리가 3개 4개 라면 동적으로 쿼리를 생성해서 처리할 수 있는 기능이 필요하다.
이러한 기능을 얻기 위해 우리는 QueryDsl을 사용한다.
즉, QueryDsl은 복잡한 검색조건이나 조인,서브 쿼리 등의 기능도 구현이 가능하다.

JPQL

JPQL을 간단하게 집고 가자면 보통 Table을 대상으로 Query를 하지만 JPQL은 Entity를 대상으로 Query한다.
JPA에서 제공하는 메소드 호출만으로 섬세한 Query작성이 어렵다는 문제를 해소하기 위해 탄생됬다.

QueryDsl의 장점과 단점

  • 장점
    1.코드로 쿼리를 작성함으로써 문법오류를 쉽게 확인 할 수 있다.
    2.자동완성 IDE의 도움을 받을 수 있다.
    3.동적 쿼리 작성이 편리하다.
    4.쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용 가능
  • 단점
    1.다소 번거로운 Gradle 설정 및 사용법 등을 익혀야한다.

QueryDsl 적용

  • build.gradle 상단
buildscript {
    ext {
        queryDslVersion = '5.0.0'
    }
}
  • build.gradle 하단
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
}
  • 의존성 추가
implementation 'com.querydsl:querydsl-jpa'
  • 위 코드를 추가 후 Gradle reLoad
  • build/generated 하위에 @Entity annotation이 붙은 클래스들이 Q클래스로 생성된것을 확인
  • 이러한 클래스를 Q클래스라고 합니다.

Q클래스란?

QueryDsl에서는 엔티티로 설정된 클래스에 Q모델이라는 쿼리타입 클래스를 미리 생성해놓고 메타데이터로 사용하여 쿼리를 메소드 기반으로 작성합니다.
쉽게 말하자면 queryDsl 라이브러리를 사용하면서 쿼리문을 작성하려면 Q타입 클래스가 필요합니다.

예제

public interface PostRepository extends JpaRepository<Post, Long> {

    @Query("select p from Post p join fetch p.comments")
    List<Post> findAllInnerFetchJoin();

    @Query("select distinct p from Post p join fetch p.comments")
    List<Post> findAllInnerFetchJoinWithDistinct();

}
  • 위 코드는 우리가 지금까지 사용하던 정적쿼리 입니다.

    @Configuration
    public class QueryDslConfig {
    
       @PersistenceContext
       private EntityManager entityManager;
    
       @Bean
       public JPAQueryFactory jpaQueryFactory() {
           return new JPAQueryFactory(entityManager);
       }
    }
    ``'
  • 프로젝트에서 QueryDsl을 사용하기 위해 JPAQueryFactory를 Bean으로 등록

public interface PostCustomRepository {

    List<Post> findAllInnerFetchJoin();

    List<Post> findAllInnerFetchJoinWithDistinct();
}
  • 기존의 메서드를 삭제하고 PostCustomRepository를 상속 받는 PostCustomRepositoryImpl 생성합니다.
@Repository
public class PostCustomRepositoryImpl implements PostCustomRepository {

    private final JPAQueryFactory jpaQueryFactory;

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

    @Override
    public List<Post> findAllInnerFetchJoin() {
        return jpaQueryFactory.selectFrom(post)
            .innerJoin(post.comments)
            .fetchJoin()
            .fetch();
    }

    @Override
    public List<Post> findAllInnerFetchJoinWithDistinct() {
        return jpaQueryFactory.selectFrom(post)
            .distinct()
            .innerJoin(post.comments)
            .fetchJoin()
            .fetch();    
    }
}

0개의 댓글