join(조인 대상, 별칭으로 사용할 Q타입)
join(innerJoin), leftJoin, rightJoin
예) teamA에 소속된 모든 회원 조회
List<Member> result = queryFactory
.selectFrom(member)
.join(member.team, team)
.where(team.name.eq("teamA"))
.fetch();
연관관계가 없는 필드와 조인
List<Member> result = queryFactory
.select(member)
.from(member, team)
.where(member.username.eq(team.name))
.fetch();
예) team이름과 member이름이 같은 회원 조회(from절에 조인할 엔티티 나열, *외부 조인 불가능)
on절을 활용한 조인(JPA2.1부터 지원)
1) 조인 대상 필터링
예) 회원과 팀 조인하되 teamA인 회원만 팀 조회
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'
leftJoin과 on절
public void join_on_filtering() {
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.leftJoin(member.team, team).on(team.name.eq("teamA"))
.fetch();
for (Tuple tuple: result) {
System.out.println("tuple: " + tuple);
}
}
실행 결과
tuple: [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple: [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]
tuple: [Member(id=5, username=member3, age=30), null]
tuple: [Member(id=6, username=member4, age=40), null]
teamA가 아닌 member의 경우 team이 null * 외부조인이 꼭 필요한 경우만 사용 권장
innerJoin과 on절
public void join_on_filtering() {
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.join(member.team, team).on(team.name.eq("teamA"))
.fetch();
for (Tuple tuple: result) {
System.out.println("tuple: " + tuple);
}
}
실행 결과
tuple: [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple: [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]
teamA가 아닌 member가 제외됨
innerJoin과 where절
public void join_on_filtering() {
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
.join(member.team, team).where(team.name.eq("teamA"))
.fetch();
for (Tuple tuple: result) {
System.out.println("tuple: " + tuple);
}
}
실행 결과
tuple: [Member(id=3, username=member1, age=10), Team(id=1, name=teamA)]
tuple: [Member(id=4, username=member2, age=20), Team(id=1, name=teamA)]
위의 결과와 동일
* 내부조인의 경우 where절로 해결 권장
2) 연관관계가 없는 외부조인
하이버네이트5.1부터 on을 사용해서 서로 관계가 없는 필드로 외부 조인하는 기능 추가됨
예) 회원과 팀 이름이 같은 경우 조회
public void join_on_no_relation() {
List<Tuple> result = queryFactory
.select(member, team)
.from(member)
//.leftJoin(member.team, team) 일반적인 경우
.leftJoin(team) // 연관관계가 없을 때
.on(member.username.eq(team.name))
.fetch();
for (Tuple tuple: result) {
System.out.println("tuple: " + tuple);
}
}
join절에 엔티티가 하나만 들어감
on 조건에 맞는(team 이름과 member의 이름이 같은 경우)만 join하여 가져옴
실행 결과
tuple: [Member(id=3, username=member1, age=10), null]
tuple: [Member(id=4, username=member2, age=20), null]
tuple: [Member(id=5, username=member3, age=30), null]
tuple: [Member(id=6, username=member4, age=40), null]
tuple: [Member(id=7, username=teamA, age=0), Team(id=1, name=teamA)]
tuple: [Member(id=8, username=teamB, age=0), Team(id=2, name=teamB)]
tuple: [Member(id=9, username=teamC, age=0), null]
조건에 맞지 않는 경우 team은 null
페치 조인은 SQL에서 제공하는 기능이 아니라 SQL조인을 활용해서 연관된 엔티티를 SQL 한번에 조회하는 기능으로 주로 성능 최적화에 사용하는 방법이다.
Member member = queryFactory
.selectFrom(member)
.join(member.team, team).fetchJoin()
.where(member.username.eq("member1"))
.fetchOne();
조인절 뒤에 fetchJoin()
밖의 쿼리와 서브 쿼리의 테이블 별칭의 별칭은 다르게 두어야함으로 Qtype별칭을 따로 지정해주어야함
QMember memberSub = new QMember("memberSub");
예) 나이가 평균 이상인 회원 조회
JPAExpressions
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.goe(
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub)
))
.fetch();
static import 활용
import static com.querydsl.jpa.JPAExpressions.select;
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.goe(
select(memberSub.age.avg())
.from(memberSub)
))
.fetch();
IN절
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.in(
select(memberSub.age)
.from(memberSub)
.where(memberSub.age.goe(10))
))
.fetch();
select절에 서브쿼리
List<Tuple> list = queryFactory
.select(member.username,
JPAExpressions
.select(memberSub.age.avg())
.from(memberSub))
.from(member)
.fetch();
// static import
import static com.querydsl.jpa.JPAExpressions.*;
List<Tuple> list = queryFactory
.select(member.username,
select(memberSub.age.avg())
.from(memberSub))
.from(member)
.fetch();
화면에 맞춘 쿼리는 좋지 않다! 쿼리 재사용성이 떨어지고 쿼리가 복잡해짐
from 절의 서브쿼리 불가 - JPA JPQL는 from 절의 서브쿼리(인라인 뷰)는 지원하지 않는다. (Querydsl도 마찬가지)
1) 서브쿼리를 join으로 변경 (불가능한 경우도 있음)
2) 애플리케이션에서 쿼리 분리하여 2번 실행
3) nativeSQL 사용
select, 조건절(where), order by에서 사용 가능
// 단순한 경우
List<String> result = queryFactory
.select(member.age
.when(10).then("열살")
.when(20).then("스무살")
.otherwise("기타"))
.from(member)
.fetch();
// 복잡한 경우
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();
마찬가지로 case문에서 구간별로 별칭을 다닌 등의 로직은 쿼리가 아닌 애플리케이션단에서 하는 것이 좋음
상수
List<Tuple> result = queryFactory
.select(member.username, Expressions.constant("A"))
.from(member)
.fetch();
Expressions.constant() 사용
문자열
// {username}_{age}
List<String> result = queryFactory
.select(member.username.concat("_").concat(member.age.stringValue())) //int -> string
.from(member)
.where(member.username.eq("member!"))
.fetch();
enum 처리 시 자주 사용
[SPRING] Querydsl | 동적쿼리 (0) | 2021.05.17 |
---|---|
[SPRING] Querydsl | 프로젝션과 결과 반환 (0) | 2021.05.17 |
[SPRING] Querydsl | 기본 문법(1) (0) | 2021.04.30 |
[SPRING] Querydsl | queryDSL이란 (0) | 2021.04.29 |
[SPRING] JPA 활용2 | OSIV와 성능 최적화 (0) | 2021.04.21 |
댓글 영역