Querydsl
Querydsl 이란?
- 정적 타입을 이용해서 SQL과 같은 쿼리를 생성할 수 있도록 해주는 프레임워크
- JPA나 Spring Data JPA와 같이 문자열로 JPQL을 작성하는 것과는 다르게 자바코드로써 SQL 쿼리를 작성할 수 있는 것
- 즉, SQL, JPQL을 자바 코드로 작성할 수 있도록 도와주는 오픈소스 빌더 API
- 장점
- 쿼리를 자바 코드로 작성 가능 (+ 쉬운 SQL 스타일 문법, 재사용 가능)
- 그에 따라 문법 오류를 접근 시점이 아닌 컴파일 시점에 확인 가능 (자바 코드로 작성하기 때문! → 자바 컴파일러가 문법 오류를 잡아줌!)
- jpql 같은 경우 sql query를 문자열로 작성하기 때문에 컴파일 시점에 오류를 발견할 수 없음! (스프링 데이터 JPA의
@Query
는 가능)
- jpql 같은 경우 sql query를 문자열로 작성하기 때문에 컴파일 시점에 오류를 발견할 수 없음! (스프링 데이터 JPA의
- 동적 쿼리 가능
사용해야 되는 이유
- 최신 자바 백엔드 기술은 스프링 부트 + JPA + 스프링 데이터 JPA 기술들을 조합해서 사용하는 추세
- 하지만 이런 기술들로는 해결하지 못하는 한계점 존재 → 복잡한 쿼리, 동적 쿼리 (실무에서 복잡한 쿼리와 동적 쿼리는 필수적으로 사용됨)
- Querydsl을 사용하게 되면 복잡한 쿼리, 동적 쿼리를 자바 코드로 해결해 낼 수 있음
Querydsl 설정 [중요]
- Querydsl은 설정이 좀 까다로우며 버전에 따라 설정이 계속 변하여 버전 업데이트 시 항상 설정 부분에 유의해야 됨
- Querydsl 설정 (for Querydsl 5.0.0)
build.gradle
에서 Querydsl을 사용할 수 있도록buildscript
,plugins
,dependencies
, +별도의 세팅 을 추가해주어야 함-
buildscript 설정 (버전 명시용)
buildscript { ext { queryDslVersion = "5.0.0" } }
-
plugins 추가 (프레임워크로써 사용할 수 있게끔 설정)
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
-
dependecies 추가 (의존관계 추가 → 직접 가져와서 사용할 수 있게끔 설정)
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
- 이전 버전에서는 annotationProcessor가 필요없었지만, 5.0.0 에선 필요
-
부가 설정 (Q-Type을 뽑아내기 위한 설정)
def querydslDir = "$buildDir/generated/querydsl" // 저장 경로 querydsl { jpa = true querydslSourcesDir = querydslDir } sourceSets { // src 폴더에 해당 경로를 자동 import -> Q-Type을 쓸 수 있게끔 main.java.srcDir querydslDir } configurations { querydsl.extendsFrom compileClasspath } compileQuerydsl { options.annotationProcessorPath = configurations.querydsl }
-
전체 build.gradle
buildscript { ext { queryDslVersion = "5.0.0" } } plugins { id 'org.springframework.boot' version '2.7.3' id 'io.spring.dependency-management' version '1.0.13.RELEASE' // querydsl 추가 id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" id 'java' } group = 'spring' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' // querydsl 추가 implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}" // p6spy (qeury log) implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.0' // lombok for main compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // lombok for test testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() } // querydsl 추가 세팅 시작 def querydslDir = "$buildDir/generated/querydsl" // 저장 경로 querydsl { jpa = true querydslSourcesDir = querydslDir } sourceSets { // src 폴더에 해당 경로를 자동 import -> Q-Type을 쓸 수 있게끔 main.java.srcDir querydslDir } configurations { querydsl.extendsFrom compileClasspath } compileQuerydsl { options.annotationProcessorPath = configurations.querydsl } // querydsl 세팅 끝
- Querydsl 동작 확인 (Q-Type 생성 확인)
- 먼저 External Libraries에서 querydsl 관련된 것들이 implementation 되었다면 잘 받아와 진 것
- 그 후 임시 Entity 하나를 생성하고
Gradle → other → complieQuerydsl
을 동작시켜 해당 Entity에 대한 Q-Type이 지정된 경로(여기선 build/generated/querydsl)에 생성되는지 확인 (Hello → QHello)- 터미널로 실행하고자 하면
./gredlew complieQuerydsl
or./gredlewJava
- 생성된 모든 Q-Type을 제거하고자 하면
./gredlew clean
- 참고로 해당 생성된 파일들은 git에 올리면 안됨 (build 하위에 생성되게 설정하여 git에 올라가지 않게 함)
- [중요] Querydsl을 사용하려면 항상 Entity의 Q-Type이 필요하기에 Entity 생성 후 항상 complieQuerydsl을 실행해 주어야 함
- 터미널로 실행하고자 하면
- 위의 모든 과정이 제대로 동작한다면 Querydsl 사용 가능!
- Querydsl 라이브러리 살펴보기
com.querydsl:querydsl-apt:5.0.0
→ Q-Type 을 generate 하는 용도의 librarycom.querydsl:querydsl-jpa:5.0.0
→ 실제 웹 어플리케이션 작성할 때 필요한 library (jpa 특화용. sql,Mongodb 등 다양하게 지원)
사용 도메인
-
엔티티
-
ERD(Entity Relationship Diagram)
-
연관관계 정의
- Member ↔ Team
- 다대일 ↔ 일대다 양방향 관계
-
Member Entity
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id") private Team team;
@ManyToOne(fetch = FetchType.LAZY)
: Team Entity와 다대일 관계를 표시, 지연로딩을 통해 Proxy객체가 설정될 수 있도록 세팅@JoinColumn(name = "team_id")
: DB Table의 team_id col과 매핑하여 PK로 사용하겠다는 뜻. → 연관관계의 주인 설정
-
Team Entity
@OneToMany(mappedBy = "team") List<Member> members = new ArrayList<>();
@OneToMany(mappedBy = "team")
: Member Entity와 일대다 관계. 연관관계의 주인이 아니며 Member의 team에 의해 매핑된 field 라고 선언
- Member ↔ Team
기본 문법
JPAQueryFactory
- Querydsl을 실행해주는 역할 (JPA로 치면 EntityManger같은 역할)
- 사용법 2가지
-
new 오퍼레이션을 통해 인스턴스 할당
@Repository public class MemberJPARepository { @PersistenceContext private EntityManager em; private final JPAQueryFactory queryFactory; public MemberJPARepository() { this.queryFactory = new JPAQueryFactory(em); }
- 원래는 생성자에서
MemberJPARepository(EntityManger em)
으로 할 수 있었으나 EntityManger를 JPA에서 주입시키기 때문에 먼저 JPA가 EntityManger를 주입(@PersistenceContext
)시킨 후에 해당 em 을 JPAQueryFactory에 할당해주어야 함
- 원래는 생성자에서
-
[권장] Bean으로 등록해서 의존성 주입(Dependency Injection)하여 사용
-
최상단 Application(
@SpringBootApplication
)에서 JPAQueryFactory를 Bean으로 등록@SpringBootApplication public class QuerydslApplication { @PersistenceContext EntityManager em; @Bean //빈으로 등록 JPAQueryFactory jpaQueryFactory() { return new JPAQueryFactory(em); } public static void main(String[] args) {...} }
- 원래는 생성자에서
MemberJPARepository(EntityManger em)
으로 할 수 있었으나 EntityManger를 JPA에서 주입시키기 때문에 먼저 JPA가 EntityManger를 주입(@PersistenceContext
)시킨 후에 해당 em 을 JPAQueryFactory에 할당해주어야 함
- 원래는 생성자에서
-
[권장] 혹은 Config(
@Configuration
)에서 JPAQueryFactory를 Bean으로 등록@Configuration public class QuerydslConfig { @PersistenceContext private EntityManager em; @Bean //빈으로 등록 JPAQueryFactory jpaQueryFactory() { return new JPAQueryFactory(em); } }
-
[권장] 마지막으로 Repository단에 의존성 주입
@Repository @RequiredArgsConstructor public class MemberJPARepository { private final EntityManager em; private final JPAQueryFactory queryFactory; ... }
- JPAQueryFactory가 bean으로 등록되었기 때문에 생성자로 주입이 가능하며, 따라서
@RequiredArgsConstructor
를 통해서 자동 주입 가능!
- JPAQueryFactory가 bean으로 등록되었기 때문에 생성자로 주입이 가능하며, 따라서
-
-
기본 Q-Type 활용
- Querydsl을 사용하기 위해선 항상 JPAQueryFactory + Entity의 Q-Type의 인스턴스를 이용하여 코드를 작성해주어야 함
- 이런 Q-Type Class의 인스턴스를 사용하는 방법은 2가지
- 별칭을 직접 지정하여 인스턴스 생성
QMember qMember = new QMember("m");
→ SQL로 치면 as 를 통해 별칭을 주는 역할from QMember as m
으로 SQL을 짜는 거라고 생각하면 됨
- 가장 기본적인 방법
- [중요] 잘 사용되지는 않지만, 같은 테이블(Entity)에 대한 Join, SubQuery를 실행할 때 각각을 구분하기 위해 사용됨 → 즉, 인스턴스를 2개 할당해야 될 때 (같은 인스턴스를 Join하거나 subQuery로 이용할 경우 충돌! ⇒ Querydsl - Advanced 의 subQuery 부분 참고)
- [권장] 기본 인스턴스 사용
QMember qMember = QMember.member;
- 자주 사용되는 방법
- Q-Type에서 기본으로 제공하는 인스턴스를 사용하는 것 → SQL로 치면 as 를 통해 별칭을 자동으로 주도록 하는 것
- static import 를 통해
QMember.member
→member
로 사용하는 것을 권장
-
권장 방법 예시
import static study.querydsl.entity.QMember.*; Member findMember = queryFactory .select(*member*) // 원래 QMember.member .from(*member*) .where(*member*.username.eq("member1")) .fetchOne();
기본 조회
List<Member> all = queryFactory
.select(*member*) // QMember.member
.from(*member*)
.fetchAll();
- JPQL의 기본 조회
“select m form Member m”
을 Querydsl을 통해 자바코드로 작성 .select()
- Querydsl 에서 자바코드를 통해 조회하는 방법
- 항상 마지막에는
fetch(), fetch…()
을 통해 직접 가져와야 함 (순수 JPA의.getResultList()
등과 같은 역할) .selectFrom()
을 통해from
절과 통합 가능- distinct를 사용하고 싶으면
select()
바로 뒤에 체인 형식으로distinct()
적용해주면 됨 →.select(…).distinct().from(…)
참고 : Querydsl의 결과로 실행되는 JPQL 확인 방법 → application.yml 에서
spring.jpa.properties.hibernate.use_sql_comments: true
설정 추가
검색 조건 쿼리
Member result = queryFactory
.selectFrom(member)
.where(member.username.eq("member1")
.and(member.age.between(10, 20)))
.fetchOne();
- JPQL의
“where …"
을 Querydsl을 통해 자바코드로 작성 - 검색 조건
member.username.eq("member1")
- to JPQL :
m.username = ?1
,.setParameter(”member1”)
- 체인 형식으로 field에 조건 및 파라미터 자동 바인딩 가능
- to JPQL :
.and(member.age.between(10, 20))
- to JPQL :
and m.age between 10 , 50
.and()
와 같이 체인 형식으로 조건 추가 가능 (.or()
도 가능)
- to JPQL :
- 결과
.where(member.username.eq("member1").and(member.age.between(10, 20)))
- to JPQL :
where m.username = ?1 and m.age between 10 and 50
- 추가로
where()
절의.and()
는‘,’
로도 표현 가능 →.where(member.username.eq("member1") , member.age.between(10, 20))
- 해당 파라미터에 null이 들어갈 경우 무시 →
where(member.username.eq("member1"), null)
⇒ null은 무시 - 이 표현식은 동적 쿼리 적용 시 굉장히 유용함
- 해당 파라미터에 null이 들어갈 경우 무시 →
-
JPQL이 제공하는 모든 검색 조건 제공
member.username.eq("member1") // username = 'member1' member.username.ne("member1") // username != 'member1' member.username.eq("member1").not() // username != 'member1' member.username.isNotNull() // name is not null -> 이름이 null이 아닌것만 member.age.in(10, 20) // age in (10,20) member.age.notIn(10, 20) // age not in (10, 20) member.age.between(10,30) // between 10 and 30 member.age.goe(30) // age >= 30 member.age.gt(30) // age > 30 member.age.loe(30) // age <= 30 member.age.lt(30) // age < 30 member.username.like("member%") // 기본 like 검색 %필수 member.username.contains("member") // like ‘%member%’ 검색 member.username.startsWith("member") // like ‘member%’ 검색 ...
결과 조회
List<Member> fetch = queryFactory
.selectFrom(member)
.fetch(); // .fetchOne(), .fetchFirst(), ...
- 결과를 어떻게 가져오냐에 대한 설정 (순수 JPA로 치면
.getSingleResult()
,.getResultList()
역할) fetch()
: List 조회, 데이터 없을 시 빈 리스트 반환fetchOne()
: 단건 조회- 결과 없으면
null
반환 - 결과가 둘 이상이면 Exception 발생 (
com.querydsl.core.NonUniqueResultException
)
- 결과 없으면
fetchFirst()
- 결과들 중 가장 먼저나오는 결과 하나를 반환
limit(1).fetchOne()
과 동일
~fetchResults()~
- content, count 를 구분하여 query 생성 → 2개의 query가 나감
- ver 5.0.0 부터 사용 금지 ⇒ 직접 content 쿼리와 count 쿼리를 구분해서 사용해야됨
~~fetchCount()~~
- count query
- ver 5.0.0 부터 사용 금지 ⇒
member.count()
로 대체하여 사용해야 됨
정렬
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
.orderBy()
사용- 원하는 필드에 체인형식으로
.desc()
,.asc()
를 달아 정렬 기준 선택 가능 (정렬 기준 둘 중 하나를 무조건 선택해주어야 함 → default 설정이 없음) - 추가로
nullsLast()
를 통해 null인 항목에 대한 순서 지정 가능 → null 이면 마지막으로 (nullsFirst()
: null이면 처음으로) - 핵심
desc()
,asc()
: 일반 정렬 → 정렬을 위해 필수적인 요소nullsLast()
,nullsFirst()
: null 데이터 순서 부여
페이징
List<Member> result = queryFactory
.selectFrom(member)
.orderBy(member.age.asc())
.offset(1) // 1번째 데이터부터 (0부터 시작)
.limit(2) // 최대 2건 가져오기
.fetch();
- offset 과 limit 을 통해 페이징 동작
.offset()
: 시작 데이터 설정 (몇번째 데이터부터 가져올 것인지. 0부터 시작).limit()
: 가져올 데이터 개수 설정 (몇개를 가져올 것인지)
집합 함수
Tuple tuple = queryFactory
.select(
member.count(), // 개수
member.age.sum(), // 합
member.age.min(), // 최소값
member.age.max(), // 최대값
member.age.avg() // 평균
)
.from(member).fetchOne();
- SQL에서 지원하는 대부분의 함수 사용 가능
- JPQL과는 다르게 field에 체인형식으로 적용
Tuple
- 단일 객체의 결과가 아니기 때문에 Tuple로 반환됨
get()
을 통해 값 접근 가능 →tuple.get(*member*.count())
- Querydsl - Advanced 파트 참고
그룹화
List<Tuple> result = queryFactory
.select(team.name, member.age.avg())
.from(member)
.join(member.team, team)
.groupBy(team.name)
.fetch();
- groupBy를 통해서 grouping 진행
- ex) member에 team을 join 시킨 후 team의 name으로 그룹화를 시킨 후 (teamA : {member1, member2}, teamB : {…}) member의 age를 평균내어 반환 → [{teamA, 20},{teamB,30}]
.groupBy()
- 그룹화하고자 하는 field 부여
-
.having()
도 사용 가능 → 그룹화된 결과 필터링.groupBy(team.name) .having(team.name.eq("teamA"))
조인
- 내부 조인(inner join) :
.join()
,.innerJoin()
- left 외부 조인(left outer join) :
.leftJoin()
- right 외부 조인(right outer join) :
.rightJoin()
-
기본 조인
List<Member> result = queryFactory .selectFrom(member) .join(member.team, team) .where(team.name.eq("teamA")) .fetch();
join(
조인 대상,
조인할 Q타입)
- 다른 테이블 조인 →
.join(member.team, team)
⇒join Team t on m.team_id = t.id
- 같은 테이블 조인
- 새로운 별칭(구분을 위해)으로 Q-Type 생성 →
QMember sub = new QMember(”sub”)
- 그 후 join →
.join(member, sub)
⇒join Member sub on m.id = sub.id
- 새로운 별칭(구분을 위해)으로 Q-Type 생성 →
- 다른 테이블 조인 →
-
세타 조인 (막 조인)
List<Member> result = queryFactory .select(member) .from(member, team) // 세타 조인 (inner join. 외부 조인 불가능) .where(member.username.eq(team.name)) .fetch();
- 연관관계가 없는 필드로 조인 →
cross join
- 일명 막조인
- 외부 조인 불가능 (hibernate를 사용하면 Join on절을 통해 가능)
- null 일 발생하거나 이런 Exception 때문이 아닌, from()을 통한 세타조인에서 지원을 안하는 것
- 하이버네이트 5.1부터 on 을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능이 추가
.from(member, team)
- from 절에 여러 엔티티를 선택해서 세타 조인
- JPQL :
select m from Member m, Team team
- SQL :
selet …. from member m **cross join team t**
.from(member).join(team)
으로도 막조인 가능 (join(member.team, team)
이 아닌 그냥 조건 없이 team을 join 하는 것)
- member의 row X team의 row 의 결과가 생성됨 (where 전)
- 연관관계가 없는 필드로 조인 →
- ON절
- ON절을 활용한 조인(JPA 2.1부터 가능)
-
조인 후 where을 통한 필터링이 아닌, 조인 하는 시점에 on을 통해 필터링 하고자 할 때 사용 (ex. 회원과 팀을 조인하면서, 팀 이름이 teamA인 팀만 조인, 회원은 모두 조회)
List<Tuple> result = queryFactory .select(member, team) .from(member) .leftJoin(member.team, team).on(team.name.eq("teamA")) .fetch();
- JPQL:
SELECT m, t FROM Member m LEFT JOIN m.team t **on t.name = 'teamA'**
- SQL:
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id **and t.name='teamA'**
- 필터링 시 outer join을 사용하고자 할때 on은 필수 → where을 통해 join 후 필터링이 불가능( ← Null) ⇒ 그래서 on을 통해 join 하는 시점에 필터링을 진행하는 것 (join할 것을 가져올때 필터링함)
- 필터링 시 inner join을 사용하고자 할 때는 on은 필수가 아님 → where을 통해 join 후 필터링 가능 ⇒
.join(member.team, team).where(team.name.eq("teamA"))
- JPQL:
-
연관관계가 없는 엔티티를 외부 조인하고자 할 때 사용 (세타외부조인. ex. 회원의 이름과 팀의 이름이 같은 대상 외부 조인)
List<Tuple> result = queryFactory .select(member, team) .from(member) .leftJoin(team).on(member.username.eq(team.name)) // 주의 : 연관관계가 없기때문에 leftJoin()에 team Entity 하나만 들어감 .fetch();
- JPQL:
SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name
- SQL:
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name
- 하이버네이트 5.1부터 on 을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능이 추가됨
- join 할 때, 해당 Member의 이름과 동일한 Team만 join이 되는 것. join되지 않은 Member도 반환 (외부 조인)
leftJoin(team)
- 연관관계가 없기때문에
leftJoin()
에 team Entity 하나만 들어감 - 일반조인:
leftJoin(member.team, team)
↔ 세타 on 조인:from(member).leftJoin(team).on(xxx)
- 연관관계가 없기때문에
- JPQL:
-
Fetch Join
Member findMember = queryFactory .selectFrom(member) .join(member.team, team).fetchJoin() .where(member.username.eq("member1")) .fetchOne();
join()
,leftJoin()
등 조인 기능 뒤에fetchJoin()
이라고 추가- join 뒤에
fetchJoin()
설정을 통해 가능 - member만 조회했지만, member와 지연로딩으로 연관된 team까지 member에 넣어주는 것
- join 뒤에
select(member)…
- 일반 조인 →
select m.*
- 패치 조인 →
select m.*, t.*
- 일반 조인 →
- 조인을 활용해서 연관된 엔티티를 SQL 한번에 조회하는 기능 (SQL에서 제공하는 기능은 아님)
- 지연 로딩의 연관 Entity를 마치 즉시로딩처럼 한번에 가져오는 기능 (1+N 문제 해결 가능)
서브 쿼리
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq( // goe 등 다양하게 사용 가능
JPAExpressions
.select(memberSub.age.max()) // avg 가능
.from(memberSub)
))
.fetch();
com.querydsl.jpa.JPAExpressions
사용JPAExpressions
뒤에 보통 Querydsl 짜듯이 select, from 을 체인형식으로 작성해주면 됨JPAExpressions.*
를 static import 하여 바로.select()
로도 사용 가능
QMember memberSub = new QMember("memberSub");
- 서브쿼리 사용 시 똑같은 인스턴스를 서브쿼리에 넣으면 충돌 발생 → 구분되는 새로운 인스턴스 생성 필요
- SQL로 치면 as 를 통해 별칭을 주는 역할 (
from QMember as m
으로 SQL을 짜는 거라고 생각하면 됨) - 결과적으로
QMember as member1, QMember as memberSub
두가지 인스턴스를 활용하는 것
-
where()
절 안에 사용하는 것 뿐만 아니라select()
절 안에서도 사용 가능List<Tuple> result = queryFactory .select(member.username, JPAExpressions .select(memberSub.age.avg()) .from(memberSub) ).from(member) .fetch();
- 하이버네이트 구현체에서 가능한 것
- But, from절에선 사용 불가능! → JPA JPQL에서 지원하지 않으므로
- 서브쿼리를 join으로 변경하여 해결하거나 쿼리를 2번 분리해서 실행하는 등의 방법으로 해결해야 됨 (웬만하면 대안 가능)
Case 문
- select, 조건절(where), order by에서 사용 가능
-
간단 Case절
List<String> result = queryFactory .select(member.age .when(10).then("열살") .when(20).then("스무살") .otherwise("기타")) .from(member) .fetch();
- filed에 체인형식으로 case절 작성
-
복잡한 Case조건
List<String> result2 = queryFactory .select(new CaseBuilder() .when(member.age.between(0, 20)).then("0~20살") .when(member.age.between(21, 30)).then("21~30살") .otherwise("기타")) .from(member) .fetch();
CaseBuilder
에 체인형식으로 case절 작성
-
[중요] Case문 + Orderby
NumberExpression<Integer> rankPath = new CaseBuilder() .when(member.age.between(0, 20)).then(2) .when(member.age.between(21, 30)).then(1) .otherwise(3); List<Tuple> result = queryFactory .select(member.username, member.age, rankPath) .from(member) .orderBy(rankPath.desc()) .fetch();
- Case를 통해 Sort 기준을 줄 때 사용
- Querydsl은 자바 코드로 작성하기 때문에 rankPath 처럼 복잡한 조건을 변수로 선언해서 select 절, orderBy 절에서 함께 사용
상수 추가
List<Tuple> addString = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetch();
Expressions.constant("A")
“A”
라는 상수를member.username
와 함께 결과에 추가- 결과 : [{”member1”, “A”}, {”member2”, “A”}, … ]
- constant 를 사용하게 되면 SQL에서 A를 생성해서 가져오는 것이 아니라 SQL에서 member.username을 가져온 결과에 “A”를 추가하는 것 → 최적화
문자 더하기
String concat = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue())) // : member.age.stringValue() 부분이 중요 (int -> String)
.from(member)
.where(member.username.eq("member1"))
.fetchOne();
.concat()
: 애초에 지정문자를 합쳐서 조회해 오는 것.stringValue()
: 문자로 만들어주는 기능 (중요: ENUM 처리 시 자주 사용!) → SQL의 cast() 역할- 결과 : member1_10