[Querydsl] 기본 문법3
도메인 모델과 테스트 기본 코드는 전 포스트 내용을 이어간다.
예제 도메인 모델
목차
- 서브 쿼리
- eq
- goe
- 여러 건 처리(in 사용)
- select절에 subQuery
- static import
- Case 문
- 상수, 문자 더하기
서브 쿼리
com.querydsl.jpa.JPAExpressions
사용
eq 사용
@Test
void subQuery() {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub)
))
.fetch();
assertThat(result)
.extracting("age")
.containsExactly(40);
}
goe 사용
@Test
void subQueryGoe() {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.goe(
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub)
))
.fetch();
assertThat(result)
.extracting("age")
.containsExactly(30,40);
}
여러 건 처리(in 사용)
@Test
void subQueryIn() {
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.in(
JPAExpressions
.select(memberSub.age)
.from(memberSub)
.where(memberSub.age.gt(10))
))
.fetch();
assertThat(result)
.extracting("age")
.containsExactly(20,30,40);
}
select 절에 subquery
@Test
void selectSubQuery() {
QMember memberSub = new QMember("memberSub");
List<Tuple> fetch = queryFactory
.select(member.username,
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub)
).from(member)
.fetch();
for (Tuple tuple : result) {
System.out.println("username = " + tuple.get(member.username));
System.out.println("age = " +
tuple.get(JPAExpressions.select(memberSub.age.avg())
.from(memberSub)));
}
}
결과
username = member1
age = 25.0
username = member2
age = 25.0
username = member3
age = 25.0
username = member4
age = 25.0
static import 활용
import static com.querydsl.jpa.JPAExpressions.select;
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(select(memberSub.age.max())
.from(memberSub)
))
.fetch();
from 절의 서브쿼리 한계
JPA JPQL 서브쿼리의 한계점으로 from 절의 서브쿼리는 지원하지 않는다.(Querydsl도 마찬가지)
하이버네이트 구현체를 사용하면 select절의 서브쿼리는 지원한다.(Querydsl도 마찬가지)
from 절의 서브쿼리 해결방안
- 서브쿼리를 join으로 변경한다.(불가능한 상황도 있다.)
- 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
- nativeSQL을 사용한다.
Case 문
select, where, orderBy에서 사용 가능
단순한 조건
@Test
void basicCase() {
List<String> result = queryFactory
.select(member.age
.when(10).then("열살")
.when(20).then("스무살")
.otherwise("기타"))
.from(member)
.fetch();
}
복잡한 조건
@Test
void complexCase() {
List<String> result = 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();
}
orderBy에서 Case문 사용
- 다음과 같은 임의의 순서로 회원을 출력하고 싶다면?
- 0 ~ 30살이 아닌 회원을 가장 먼저 출력
- 0 ~ 20살 회원 출력
- 21 ~ 30살 회원 출력
@Test
void orderByCase() {
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();
}
Querydsl은 자바 코드로 작성하기 때문에 rankPath
처럼 복잡한 조건을 변수로 선언해서 select
절, orderBy
절에서 함께 사용할 수 있다.
상수, 문자 더하기
상수
상수가 필요하면 Expressions.constant(xxx)
사용
@Test
void constant() {
Tuple result = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetchFirst();
}
[참고] : 위와 같이 최적화가 가능하면 SQL에 constant값을 넘기지 않는다. 상수를 더하는 것 처럼 최적화가 어려우면 SQL에 constant값을 넘긴다.
문자 더하기
@Test
void concat() {
String result = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue()))
.from(member)
.where(member.username.eq("member1"))
.fetchOne();
}
- 결과 : member1_10
[참고] : 문자가 아닌 다른 타입들은
stringValue()
로 문자로 변환할 수 있다. 이 방법은 ENUM을 처리할 때도 자주 사용한다.
<출처 : 인프런 - 실전! Querydsl(김영한)>
댓글남기기