QueryDSL 시작하기

QueryDSL

홍찬기
8 min readMay 31, 2021

QueryDSL이란

Querydsl 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해 주는 프레임워크다.

QueryDSL을 사용하는 이유

Querydsl은 타입에 안전한 방식으로 HQL(Hibernate Query Language)쿼리를 실행하기 위한 목적으로 만들어졌다. HQL 쿼리를 작성하다보면 String 연결을 이용하게 되고, 이는 결과적으로 읽기 어려운 코드를 만드는 문제를 야기한다. String을 이용해서 도메인 타입과 프로퍼티를 참조하다보면 오타 등으로 잘못된 참조를 하게 될 수 있으며, 이는 String을 이용해서 HQL 작성할 때 발생하는 또 다른 문제다.

타입에 안전하도록 도메인 모델을 변경하면 소프트웨어 개발에서 큰 이득을 얻게 된다. 도메인의 변경이 직접적으로 쿼리에 반영되고, 쿼리 작성 과정에서 코드 자동완성 기능을 사용함으로써 쿼리를 더 빠르고 안전하게 만들 수 있게 된다.

QueryDSL 원칙

  1. Querydsl의 핵심 원칙은 타입 안정성(Type safety)이다. 도메인 타입의 프로퍼티를 반영해서 생성한 쿼리 타입을 이용해서 쿼리를 작성하게 된다. 또한, 완전히 타입에 안전한 방법으로 함수/메서드 호출이 이루어진다.
  2. 또 다른 중요한 원칙은 일관성(consistency)이다. 기반 기술에 상관없이 쿼리 경로와 오퍼레이션은 모두 동일하며, Query 인터페이스는 공통의 상위 인터페이스를 갖는다.

모든 쿼리 인스턴스는 여러 차례 재사용 가능하다. 쿼리 실행 이후 페이징 데이터와 프로젝션 정의는 제거된다.

QueryDSL 시작하기

  • Gradle설정
plugins { 
...
id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}
dependencies {
...
implementation 'com.querydsl:querydsl-jpa'
}
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
jpa = true
querydslSourcesDir = querydslDir
}

sourceSets {
main.java.srcDir querydslDir
}

compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}

configurations {
querydsl.extendsFrom compileClasspath
}
  • Q파일 생성

Gradle을 설정하고 다운로드를 받고 오른쪽에 Gradle이라고 써져있는것을 누르면 아래의 이미지의 화면을 볼 수 있다. 이때 compileQuerydsl을 더블클릭을하면 Q파일이 생성이된다.

Compile Gradle경로

Gradle을 보면 Q파일의 경로가 $buildDir/generated/querydsl 로 되어있다.
해당 경로로 들어가보면 Q파일이 생성되어있는것을 볼 수 있다.

QueryDSL 사용하기

QueryDSL을 사용하려면 JPAQueryFactory에다가 EntityManager를 주입받아 사용할 수 있다. JPAQueryFactory에 EntityManager를 주입하는 방법은 2가지 방법이 있다.

  • 파라미터에 EntityManager만 받고 생성자 안에서 JPAQueryFactory에 EntityManager를 주입하는 방법
  • JPAQueryFactory를 Bean으로 등록해서 사용하는 방법

JPA를 QueryDSL 변경하기

@Repository
public class MemberJpaRepository {

private final EntityManager em;
public MemberJpaRepository(EntityManager em) {
this.em = em;
}
public List<Member> findAll() {
return em.createQuery("select m from Member m",Member.class)
.getResultList();
}
public List<Member> findByUsername(String username) {
return em.createQuery(
"select m from Member m where m.username = :username",
Member.class)
.setParameter("username", username)
.getResultList();
}

위와같은 JPA코드는 에러를 컴파일 시점이아니라 Query요청시점에서 발견이 된다. 이것을 QueryDSL로 변경을하면 컴파일 시점에서 해당 코드가 잘못된 코드인지 판별할 수 있는 장점이 있다.

위의 JPA코드를 QueryDSL로 변경해보자.

import static study.querydsl.entity.QMember.member;@Repository
public class MemberJpaRepository {

private final EntityManager em;
private final JPAQueryFactory queryFactory;
public MemberJpaRepository(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
public List<Member> findAll_Querydsl() {
return queryFactory
.selectFrom(member)
.fetch();
}
public List<Member> findByUsername_Querydsl(String username) {
return queryFactory
.selectFrom(member)
.where(member.username.eq(username))
.fetch();
}

이때 member는 QMember.member를 static import로 적용한 member이다. static import적용한 member를 찾아가보면 아래와같이 member1의 이름으로 생성한 Qmember라는것을 볼 수 있다.

만약에 같은 테이블을 JOIN을 할 경우에는 서로다른 QMember를 생성해줘야한다. ex) member -> member1, memberSub -> memberSub

public List<Member> findByUsername_Querydsl(String username) {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
select(memberSub.age.max())
.from(memberSub)))
.fetch();
}

References

inflearn- QueryDSL 강의

--

--