TIL

JPA - QueryDSL

haseung22 2024. 11. 15. 12:08

👻 QueryDSL이란 ?

SQL, JPQL 등을 코드로  작성할 수 있도록 해주는 프레임워크이며, 쿼리를 type-safe(컴파일시 에러 체크 가능)하게 Java 코드로 작성할 수 있다.

 

JPQL 이란?
JPA에서 지원하는 다양한 쿼리 방법 중 가장 단순한 조회 방법으로, SQL의 경우에는 DB 테이블을 대상으로 쿼리를 질의하지만, JPQL은 엔티티 객체를 대상으로 쿼리를 질의

JPQL의 경우 아래 코드에서 볼 수 있듯, 쿼리를 문자열로 입력하므로 오타가 발생하면 관리가 어렵고,
type-safe 불가능하여 런타임에서 해당 쿼리가 실행되어야만 오류를 발견할 수 있다는 단점이있다.
@Query("select t from Todo t inner JOIN fetch t.user where t.modifiedAt between :startDate and :endDate order by t.modifiedAt desc")
    Page<Todo> findByModifyAt(LocalDateTime startDate, LocalDateTime endDate, Pageable pageable);

 

👉 QueryDSL 사용하는 이유

  • JPQL과 달리 문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있다.
  • 동적인 쿼리 작성이 편리하다.
  • 다양한 데이터베이스 시스템과 연동할 수 있으며, JPA, MyBatis 등 다양한 ORM 프레임워크와 연동 가능

😸 QueryDSL 설정

build.gradle

 implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"

 

Q 클래스 생성

 

빌드를 마친 후  Gradle > Tasks > other > complie.java를 실행 시키면 Q클래스 파일이 생성된다.

 

 

QueryDsl Config 설정

@Configuration
public class JpaConfiguration {

    @PersistenceContext
    private EntityManager em;

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

 

😎 간단한 예제로 QueryDSL 사용해보기

 

현재 JPQL로 작성된 findByIdWithUser 메서드를 QueryDSL로 변경해보려고한다.

    @Query("SELECT t FROM Todo t LEFT JOIN t.user WHERE t.id = :todoId")
    Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);

 

먼저 커스텀 인터페이스를 작성한다.(QueryDSL Repository 에서 해당 인터페이스를 구현한다.)

public interface CustomTodoRepository {
    Optional<Todo> findByIdWithUser(Long todoId);
}

 

구현체 클래스를 만든다.

@Repository
@RequiredArgsConstructor
public class CustomTodoRepositoryImpl implements CustomTodoRepository {

    private final JPAQueryFactory jpaQueryFactory;
    private final QTodo todo = QTodo.todo;

    @Override
    public Optional<Todo> findByIdWithUser(Long todoId) {
        return Optional.ofNullable(jpaQueryFactory.selectFrom(todo)
                                       .where(todo.id.eq(todoId))
                                       .leftJoin(todo.user, user)
                                       .fetchJoin()
                                       .fetchFirst()
        );
    }
}

 

이후에는 기존의 TodoRepository에 CustomTodoRepository도 추가로 상속 받으면 된다.

public interface TodoRepository extends JpaRepository<Todo, Long> , CustomTodoRepository{
}

 

이렇게 하면 결과적으로 TodoRepository는 JpaRepository와 QueryDSLRepository의 메서드를 전부 가질 수 있게 된다.

커스텀 리포지토리에서는 QueryDSL을 사용할 메서드만 추상화해서 가지면된다.

 

 

이렇게 기존에 JPQL로 작성되어있던 메서드를 QueryDSL로 바꿔보며 간단하게나마 사용해봤는데  지금은 단순한 쿼리문이라 그렇게 막 편해졌다!! 이런 느낌은 없지만 쿼리가 좀 더 복잡해진다면 JPQL보다는 QueryDSL을 더 자주 사용할 것 같다는 생각이 들었다

 무엇보다 자동완성을 사용할 수 있다는 점이 편했다