QueryDsl이란?
QueryDSL은 타입 안전한(Typed-safe) SQL 및 JPQL 쿼리를 생성할 수 있는 Java 기반의 쿼리 빌더 프레임워크입니다.
Spring Data JPA와 함께 사용되며, JPQL을 코드로 작성할 수 있어 가독성과 유지보수성이 뛰어나고, 컴파일 타임에서 문법 오류를 잡을 수 있는 장점이 있습니다.
왜 사용하나?
실제로 Query를 사람이 짜다보면 수많은 쿼리를 수작업으로 생성해야한다. 사람이 짜다보면 Query는 컴파일 단계에서 오류가 있는지 알 수가 없다(String으로 처리되기 때문이다) Query 생성을 자동화 하여, 자바 코드로 작성할 수 있다. 그 외 기타 이득이 많다.
QueryDSL vs JPQL vs Criteria vs Native Query
비교 항목 |
QueryDSL | JPQL | Criteria API | Native Query |
타입 안전성 | O | X | O | X |
코드 가독성 | O (직관적) | X (문자열 기반) | X (복잡함) | X (SQL 직접 작성) |
쿼리 동적 생성 | O | X | O | O |
실행 성능 | O (JPA 기반) | O (JPA 기반) | O (JPA 기반) | O (DB 직접 실행) |
유지보수성 | O | X | X | X |
JPQL 지원 | O | O | O | X (SQL만 가능) |
- QueryDSL은 JPQL의 단점을 보완하면서도 유지보수성이 뛰어난 장점이 있다.
- 동적 쿼리를 편리하게 생성할 수 있으며, 타입 안전성을 제공하여 컴파일 타임 오류를 방지한다.
QueryDSL 설정 (Spring Boot + JPA) 및 사용 예시
프로젝트 설정
Gradle
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
dependencies {
implementation 'com.querydsl:querydsl-jpa'
annotationProcessor 'com.querydsl:querydsl-apt:jpa'
}
// QueryDSL 자동 생성 설정
querydsl {
jpa = true
querydslSourcesDir = "$buildDir/generated/sources/annotationProcessor/java/main"
}
sourceSets {
main {
java {
srcDirs = ["$buildDir/generated/sources/annotationProcessor/java/main"]
}
}
}
tasks.withType(JavaCompile) {
options.annotationProcessorGeneratedSourcesDirectory = file("$buildDir/generated/sources/annotationProcessor/java/main")
}
엔티티 클래스 생성
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
@ManyToOne(fetch = FetchType.LAZY)
private Team team;
}
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.List;
@Entity
@Getter
@NoArgsConstructor
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members;
}
QueryDSL 쿼리 예제
(1) QueryDSL을 사용한 기본 조회
이 예시는 QueryDSL을 사용하여 타입 안전한 SQL 쿼리를 작성하고 Spring Data JPA와 통합하는 방법을 보여줍니다. QueryDSL을 사용하면 코드 가독성을 높이고 컴파일 시점에 쿼리 오류를 잡을 수 있습니다.
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import java.util.List;
import static com.example.entity.QMember.member;
@Repository
public class MemberRepository {
private final JPAQueryFactory queryFactory;
public MemberRepository(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
public List<Member> findByName(String name) {
return queryFactory
.selectFrom(member)
.where(member.name.eq(name))
.fetch();
}
}
- QMember.member를 사용하여 타입 안전한 쿼리를 작성 가능.
- fetch()는 리스트 반환.
(2) WHERE 절 조건 (eq, lt, gt, between, like)
public List<Member> findByAgeGreaterThan(int age) {
return queryFactory
.selectFrom(member)
.where(member.age.gt(age)) // 나이가 특정 값보다 큰 경우
.fetch();
}
public List<Member> findByNameLike(String keyword) {
return queryFactory
.selectFrom(member)
.where(member.name.like("%" + keyword + "%")) // 이름에 특정 문자열 포함
.fetch();
}
(3) JOIN (팀과 멤버 조회)
import static com.example.entity.QTeam.team;
public List<Member> findMembersByTeamName(String teamName) {
return queryFactory
.selectFrom(member)
.join(member.team, team) // INNER JOIN
.where(team.name.eq(teamName))
.fetch();
}
(4) DTO 변환 (Projection)
1) DTO 클래스 생성
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class MemberDTO {
private String name;
private int age;
}
2) QueryDSL을 사용한 DTO 조회
import com.querydsl.core.types.Projections;
public List<MemberDTO> findMemberDTO() {
return queryFactory
.select(Projections.constructor(MemberDTO.class, member.name, member.age))
.from(member)
.fetch();
}
- Projections.constructor(MemberDTO.class, ...) 사용.
(5) 동적 쿼리 (BooleanBuilder)
조건이 동적으로 변할 때 BooleanBuilder 사용 가능.
import com.querydsl.core.BooleanBuilder;
public List<Member> findMembersByDynamicQuery(String name, Integer age) {
BooleanBuilder builder = new BooleanBuilder();
if (name != null) {
builder.and(member.name.eq(name));
}
if (age != null) {
builder.and(member.age.gt(age));
}
return queryFactory
.selectFrom(member)
.where(builder)
.fetch();
}
- name 또는 age 조건이 있을 때만 WHERE 절 추가.
QueryDSL vs Spring Data JPA
비교 항목 |
QueryDSL | Spring Data JPA |
쿼리 작성 | 동적 쿼리 작성 가능 | 동적 쿼리 작성 어려움 |
타입 안전성 | O | X (문자열 기반) |
가독성 | 직관적 | JPQL 문자열 기반 |
복잡한 JOIN | O | 어려움 |
DTO 변환 | O (Projections) | JPQL에서 수동 변환 필요 |
- QueryDSL은 복잡한 동적 쿼리, JOIN, DTO 변환이 많을 때 매우 유용
- Spring Data JPA와 함께 사용하면 기본 쿼리는 Spring Data JPA, 복잡한 쿼리는 QueryDSL로 처리 가능
참고 :
https://velog.io/@jkijki12/Spring-QueryDSL-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
'개발 > Spring' 카테고리의 다른 글
[Spring] spring 기초 (0) | 2023.03.09 |
---|---|
[Spring] 어노테이션 모음집 (0) | 2023.03.08 |
[Spring] Spring Boot (0) | 2023.02.10 |
[Spring] Spring Framework (0) | 2023.02.10 |
[Spring] Spring Framework vs Spring MVC (0) | 2022.12.26 |