QueryDSL VS JPQL
QueryDSL은 자바에서 사용하는 ORM(Object-Relational Mapping) 프레임워크인 JPA(Java Persistence API)에서 사용되는 쿼리 언어입니다. 이와 동일한 역할을 하는 JPQL이란 것도 있습니다. JPA에서 사용되는 두 쿼리 언어(QueryDSL, JPQL)의 차이는 무엇일까요?
타입 안정성
JPQL은 문자열로 작성된 쿼리이기 때문에 쿼리에 문제가 있는 경우, 컴파일러가 알아내기 어렵습니다. 이에 반해 QueryDSL은 자바 언어로 작성되기 때문에 컴파일 시점에서 문제를 발견할 수 있으며, 런타임에서 오류가 발생할 가능성이 크게 줄어듭니다.
예를 들어 JPQL을 사용하여 특정 책의 저자를 찾는 코드를 작성한다고 가정해 보겠습니다. 다음은 JPQL로 작성한 코드입니다.
String jpql = "SELECT a.name FROM Book b JOIN b.author a WHERE b.id = :bookId";
TypedQuery<String> query = entityManager.createQuery(jpql, String.class);
query.setParameter("bookId", bookId);
String authorName = query.getSingleResult();
이번에는 QueryDSL를 사용하여 동일한 작업을 수행하는 코드를 작성해 보겠습니다.
String authorName = new JPAQuery<>(entityManager)
.select(book.author.name)
.from(book)
.join(book.author, author)
.where(book.id.eq(bookId))
.fetchOne();
위 코드에서는 QueryDSL을 사용하여 JPQL을 생성하고 있으며, 컴파일 시점에서 book.author.name에서 오타가 있는 경우 컴파일 오류가 발생합니다. 또한 반환 타입이 String인 것을 보장하므로 TypedQuery를 사용할 필요가 없습니다. 이렇게 타입 안정성을 보장하는 것이 QueryDSL의 장점입니다.
가독성
JPQL은 문자열로 작성되기 때문에 길이가 길어지면 가독성이 떨어집니다. 또한, 복잡한 쿼리를 작성하면 필요한 데이터만 추출하는 것이 어려워질 수 있습니다. 이에 반해 QueryDSL은 자바 언어로 작성되기 때문에 가독성이 뛰어나며, 복잡한 쿼리도 필요한 데이터만 추출하는 것이 보다 쉬워집니다.
유지보수성
JPQL은 문자열로 작성되기 때문에 쿼리를 수정할 때에는 문자열을 직접 수정해야 합니다. 이는 오타나 문법 오류가 발생할 가능성이 높아지고 유지보수성이 떨어집니다. 이에 반해 QueryDSL은 자바 언어로 작성되기 때문에 쿼리를 수정할 때에는 코드를 수정하면 됩니다. 이는 오타나 문법 오류가 발생할 가능성을 줄이고 유지보수성을 높입니다.
QueryDSL 사용 방법
QueryDSL을 사용하려면 아래의 과정을 따라야 합니다.
1. 의존성 추가
프로젝트에 QueryDSL 의존성을 추가합니다. Gradle을 사용한다면 아래와 같이 추가할 수 있습니다. '${querydsl.version}'은 사용하고자 하는 QueryDSL 버전에 맞게 변경해주어야 합니다.
dependencies {
implementation 'com.querydsl:querydsl-jpa:${querydsl.version}'
annotationProcessor "com.querydsl:querydsl-apt:${querydsl.version}:jpa"
}
2. Q 클래스 생성
QueryDSL에서는 각 엔티티 클래스의 Q 클래스를 생성해야 합니다.(Q class란 QueryDSL에서 사용하는 클래스로, 코드를 자동 생성하여 제공하는 클래스입니다. QueryDSL은 JPQL이나 SQL을 자바 코드로 작성할 수 있게 해주는 라이브러리이며, Q 클래스는 이러한 자바 코드에서 사용되는 클래스입니다. Q 클래스는 QueryDSL에서 생성한 코드로, JPA 엔티티에 해당하는 클래스를 대상으로 작성됩니다. Q 클래스는 각 엔티티에 대해 하나의 클래스가 생성되며, 이 클래스에는 해당 엔티티와 관련된 JPQL이나 SQL에서 사용하는 모든 필드와 메서드가 포함됩니다. Q 클래스의 이름은 QueryDSL이 자동으로 생성하며, 엔티티 클래스 이름 뒤에 Q를 붙인 이름을 가집니다.) 이를 통해 동적인 SQL을 생성할 때 컴파일 타임에 오류를 잡아낼 수 있습니다. Q 클래스는 'querydsl-apt' 플러그인을 사용하여 생성할 수 있습니다. Gradle 파일에 아래와 같은 코드를 추가해주어야 합니다. '${querydsl.version}'은 사용하고자 하는 QueryDSL Gradle 플러그인 버전에 맞게 변경해주어야 합니다.
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "${querydsl.gradle.version}"
}
dependencies {
def querydslVersion = "4.2.2"
implementation 'com.querydsl:querydsl-jpa:${querydsl.version}'
annotationProcessor "com.querydsl:querydsl-apt:${querydsl.version}:jpa"
}
def generated = "src/generated/java"
querydsl {
library = "com.querydsl:querydsl-apt"
jpa = true
querydslSourcesDir = file(generated)
}
sourceSets {
main.java.srcDirs += [ generated ]
}
compileJava {
dependsOn generateQuerydslSources
options.compilerArgs += "-processorpath", configurations.querydsl.dependencies.asPath
}
3. Query 생성
QueryDSL을 사용하여 동적인 SQL을 생성하려면 'JPAQueryFactory' 클래스를 사용해야 합니다. 이 클래스는 'EntityManager'를 이용하여 JPA 쿼리를 생성합니다. 아래는 QueryDSL을 사용하여 'book' 엔티티에서 'author' 이름이 'john'인 책 목록을 조회하는 코드입니다. 아래 코드에서 QMember와 QTeam은 QueryDSL이 생성한 Q-Type입니다. Q-Type은 엔티티 클래스의 필드와 연결된 메타 정보를 담고 있습니다.
queryFactory.selectFrom()를 사용하여 조회 대상 엔티티를 지정하고 'leftJoin()'으로 외부 조인을 수행하며, 'where()'로 조건을 추가합니다. 그리고 'orderBy()'로 정렬 조건을 추가한 뒤 'fetch()'로 결과를 조회합니다.
QBook book = QBook.book;
QAuthor author = QAuthor.author;
List<Book> books = queryFactory.select(book)
.from(book)
.leftJoin(book.author, author)
.where(author.name.eq("John"))
.fetch();
동일한 동작을 하는 쿼리를 JPQL로 작성하면 다음과 같습니다. QueryDSL을 사용한 코드와 비교하면, JPQL 코드는 확실히 문자열로 작성되어 있어 유지보수가 어렵고 자바 문법보다 SQL 문법에 더 가까운 모습입니다.
TypedQuery<Member> query = entityManager.createQuery(
"SELECT m FROM Member m LEFT JOIN m.team t WHERE m.age > :age ORDER BY m.name DESC",
Member.class)
.setParameter("age", 20);
List<Member> members = query.getResultList();
** 그렇다면 무조건 QueryDSL을 사용하는 것이 더 좋을까요?
JPA 사용 시 일반적으로 QueryDSL을 사용하는 것이 더 좋은 선택이 될 수 있습니다. 하지만 QueryDSL을 사용하는 것이 항상 더 좋은 것은 아닙니다. QueryDSL은 JPQL과 달리 별도의 라이브러리 별도의 라이브러리를 추가해야 하며, (JPQL은 JPA에서 지원하는 쿼리 언어이므로 JPA의 일부입니다. 따라서 별도로 의존성을 추가하지 않아도 됩니다.) JPQL 보다 학습 곡선이 높습니다. 또한 단순한 쿼리 작성이라면 JPQL로 충분할 수 있으며 오히려 QueryDSL을 도입하는 것이 과도한 부담이 될 수 있습니다. 따라서, 사용하는 프로젝트의 성격과 요구사항, 개발자의 스킬 레벨 등을 고려하여 JPQL과 QueryDSL 중 적절한 것을 선택하는 것이 좋습니다.
'JPA' 카테고리의 다른 글
JPA N+1 문제 해결 (0) | 2023.07.04 |
---|---|
JPA 연관 관계 설정 (0) | 2021.07.06 |
JPA의 개념과 이해 (0) | 2021.07.04 |