오픈소스 프로젝트로 JPQL을 Java 코드(SQL 형식의 쿼리)로 Type-Safe 하게 작성할 수 있도록 하는 라이브러리다.
@Query("SELECT t FROM Todo t " +
"LEFT JOIN t.user " +
"WHERE t.id = :todoId")
Optional<Todo> findByIdWithUser(@Param("todoId") Long todoId);
해당 위 쿼리는 JPQL로 작성되어 있다. QueryDSL로 만들어 보자
1. 휴먼 에러 방지 :
기존 JPQL은 쿼리를 문자열로 작성해야한다. 만약 오타가 있거나 잘못 작성한다해도 컴파일 시점에 에러가 발생하지 않고 런타임 시점에 발생하기 때문에 실행시키기 전에는 잘못된 부분을 알 수 없다.
하지만 QueryDSL은 자바 코드로 쿼리를 작성하기 때문에 컴파일 시점에 에러를 잡을 수 있다는 큰 장점이 있다.
2. 중복된 조건식 재활용 가능 :
기존에 같은 조건에 추가로 조건이 생긴다면, 새로운 JPQL을 작성해 작업해야 되어 조건의 중복사용 불가하여 상당히 불편하엿다.
그렇기에 QueryDSL는 Expression과 JPAQueryFactory로 나누어 중복사용이 가능해지며 편리함을 추구하게 되었다.
3. 보기 쉬운 동적쿼리 :
JPQL는 조건이 많으면 많을 수록 읽기 어려워 진다는 단점을 가지고 있었는데, QueryDSL 바뀌면서 조건이 많아도 읽기 수월하고 그만큼 깔끔한 코드를 유지 할 수 있다.
주의 : 설정은 환경마다 다르며 그렇기에 설정 역시 다를 수 있다.
작성자 작업 환경 : SpringBoot 3.4.0, Java 17
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"
위 처럼 작성 하면 Entity의 필드를 Q클래스로 자동으로 생성 해 준다.
Querydsl을 사용하면 기본적으로 QClass라는 자식이 생성된다. 이는 엔티티 클래스의 메타 데이터를 가지고 있는 클래스이다.
1. JpaQueryFactory 빈(Bean) 등록
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
2. JPA 레포지터리 선언
public interface TodoRepository extends JpaRepository<Todo, Long>, TodoRepositoryQueryDsl {
}
TodoRepositoryQueryDsl 상속시켜 레파지토리에서 사용 가능 하게 만든다.
이에 JPQL과 QueryDSL는 동시에 사용가능 하여 간단한 쿼리는 JPQL, 복잡한 쿼리는 QueryDSL로 작성하기에 용이하다.
3. TodoRepositoryQueryDsl 인터페이스 생성
public interface TodoRepositoryQueryDsl {
Optional<Todo> findByIdWithUser(Long todoId);
}
Spring Data JPA가 커스텀 구현체를 자동으로 인식 할 수 있음
코드를 역할별로 나눌 수 있고, 유지보수에 유리함
확장성이 높으며 단위 테스트에 좋다
4. TodoRepositoryQueryDslImpl 클래스 생성
@RequiredArgsConstructor
public class TodoRepositoryQueryDslImpl implements TodoRepositoryQueryDsl {
private final JPAQueryFactory jpaQueryFactory;
QTodo qTodo = QTodo.todo;
@Override
public Optional<Todo> findByIdWithUser(Long todoId) {
return Optional.ofNullable(jpaQueryFactory
.select(qTodo)
.from(qTodo)
.join(qTodo.user)
.fetchJoin()
.where(qTodo.id.eq(todoId))
.fetchOne());
}
}
문자열로 표현하던 JPQL를 대체하여 QueryDSL를 사용해서 쿼리스럽게 작성 할 수 있다.
가독성이 좋으며 휴먼 에러를 컴파일러에서 잡아 줄 수 있다.
쿼리를 보면 레포지토리에 상속하기에 JPQL과 QueryDSL를 같이 쓸 수 있다.
그렇기에 간단한 조건이라면 JPQL 구현하면 빠르게 작성 할 수 있고, 조건이 많거나 중복되는 조건이 있다면 QueryDSL로 작성해서 처리한다면 상당한 도움이 될 것이다.
참고
https://velog.io/@evan523/JPA-QueryDSL
https://www.youtube.com/watch?v=Dz-46mPfkGo&t=269s
https://turtle-codingstudy.tistory.com/54
챗GPT