이번 포스팅은 프로젝트에서 QueryDSL을 사용하면서 가끔 잊어버리는 것이 있어서 전체적으로 정리해보려 한다.
➡️QueryDSL
원하는 조건의 데이터를 수집하기 위해서는 필연적으로 JPQL을 작성하게 된다. 만약 이런과정에서 로직이 복잡한 로직의 경우 개행이 포함된 쿼리 문자열이 떨어지고 가독성이 떨어진다. JPQL 문자열에 오류가 있을 경우 정적 쿼리라면 어플리케이션 로딩 시점에 이를 발견할 수 있으나 런타임 시점에 에러가 발생한다. 이러한 문제를 QueryDSL이 해결을 해준다.
QueryDSL은 정적 타입을 이용해서 SQL 등의 쿼리를 생성해주는 프레임워크이다.
➡️QueryDSL의 장점
문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문제 오류를 빠르게 찾을 수 있다.
동적인 쿼리 작성이 편리하다.
쿼리 작성 시 제약 조건 등을 메서드 추출을 통해 재사용할 수 있다.
⚙️설정
build.gradle
plugins {
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
group = 'com'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('snippetsDir', file("build/generated-snippets"))
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.querydsl:querydsl-jpa'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
def querydslDir은 Q-class가 생성되는 경로이다.
Q-type class는 QueryDSL 설정을 성공적으로 마치면 오른쪽 상단 Gradle에 들어가 Task를 선택 후 other에 들어가 complieQueryDSL를 선택하면 @Entity가 붙은 클래스에 자동으로 생성된다.
이런식으로 생성된다.
Q-type의 클래스는 QueryDSL을 사용하여 메소드 기반으로 쿼리를 작성할 때 우리가 만든 도메인 클래스의 구조를 설명해주는 메타데이터 역할을 하며 쿼리의 조건을 설정할 때 사용된다.
이제 QueryDSL의 기본적인 문법을 보자.
1.sort
- 내림차순 정렬(desc)
List<Post> descPostId = jpaQueryFactory
.selectFrom(post)
.orderBy(post.id.desc())
.fetch();
- 오름차순 정렬(asc)
List<Post> ascPostId = jpaQueryFactory
.selectFrom(post)
.orderBy(post.id.asc())
.fetch();
- 여러 조건으로 정렬
List<Post> resultPost = jpaQueryFactory
.selectFrom(post)
.orderBy(post.id.desc(), post.title.asc())
.fetch();
- null 처리
List<Post> result= jpaQueryFactory
.selectFrom(post)
.orderBy(post.id.desc(), post.title.asc().nullsLast)
.fetch();
2.fetch
- fetch()
- 리스트로 반환하는 방법이고 만약 리스트가 비어있으면 빈 리스트를 반환한다.
List<Post> fetchList = jpaQueryFactory
.selectFrom(post)
.fetch();
- fetchOne()
- 하나를 조회할 때 사용한다. 결과가 없을 경우 null을 반환하고 둘 이상일 경우에는 NonUniqueReultException을 반환한다.
Post fetchOne = jpaQueryFactory
.selectFrom(post)
.fetchOne();
- fetchFirst()
- 가장 처음의 한건을 가져오고 싶을때 사용한다.
Post fetchFirst = jpaQueryFactory
.selectFrom(post)
.fetchFirst();
- fetchResults()
- 페이징을 위해 사용되고 total content를 가져온다.
QueryResults<Post> fetchResult = jpaQueryFactory
.selectFrom(post)
.fetchResult();
- fetchCount()
- 개수조회에 사용된다.
long fetchCount = jpaQueryFactory
.selectFrom(post)
.fetchCount();
3.paging
querydsl에서는 페이징에 필요한 정보를 가져올 수 있는 메서드가 존재한다.
QueryResults<Post> result = jpaQueryFactory
.selectFrom(post)
.orderBy(post.id.desc())
.offset(0)
.limit(3)
.fetchResult();
long total = result.getTotal();
long limit = result.getLimit();
long offset = result.getOffset();
List<Post> results = result.getResults();
간단하게 메서드를 설명하자면
total은 전체 컨텐츠 갯수이고,
offset은 조회를 시작할 위치이고,
limit은 조회 개수를 제한할 위치이다.
.getResults() 메서드는 조건에 맞게 조회된 컨텐츠 리스트이다.
4. aggregation
querydsl에서 편하게 sql 집계를 낼수 있는 기능이 있다. 실제 sql을 사용하는 것처럼 sql의 groupBy.having절을 사용할 수 있다.
List<Tuple> result = jpaQueryFactory
.select(
team.name,
user.count,
user.age.avg(),
user.age.min(),
user.age.max()
)
.from(user)
.join(user.team, team)
.groupBy(team.name)
.having(team.name.eq("A")
.fetch();
Tuple team1 = result.get(0);
String teamName = team1.get(team.name);
Long team1Cnt = team1.get(user.count());
Double team1AgeAvg = team1.get(user.age.avg());
Integer team1MinAge = team1.get(user.age.min());
Integer team1MaxAge = team1.get(user.age.max());
😸GITHUB
https://github.com/ryudongjae/Querydsl
GitHub - ryudongjae/Querydsl
Contribute to ryudongjae/Querydsl development by creating an account on GitHub.
github.com
REFERENCE
'Dev > SpringData' 카테고리의 다른 글
[JPA] 양방향 연관관계(무한 루프) (0) | 2021.12.14 |
---|---|
[Querydsl] Projection 정리 (0) | 2021.12.12 |
[Spring] DTO 사용범위에 대하여 (0) | 2021.11.12 |
[JPA]Fetch.Lazy를 설정했을때 @OneToOne에서 발생하는 이슈 (0) | 2021.10.13 |
[JPA] 엔티티 매핑 (0) | 2021.09.10 |