1. JPAExpressions
Querydsl에서 서브쿼리를 작성할 때, com.querydsl.jpa.JPAExpressions 사용해야한다.
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(memberSub.age.max())
.from(memberSub)
))
.fetch();
서브쿼리 시작할 때 JPAExpressions를 붙이면 된다.
static import해서 쓰자!
import static com.querydsl.jpa.JPAExpressions.select;
static import하고 나면 JPAExpressions를 따로 붙일 필요없이 select로 시작하면 된다.
참고로, 인텔리제이에서 Alt + Enter를 눌러도 static import 추가 버튼이 안뜬다면 설정에 들어가서 Add on-demand static import를 체크해주면 된다.
2. Q클래스 인스턴스
메인 쿼리와 서브쿼리의 조회대상 엔티티가 같다면, Q클래스 인스턴스를 생성해서 별칭을 직접 지정해줘야한다.
public void subQuery(){
QMember memberSub = new QMember("memberSub");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
select(memberSub.age.max())
.from(memberSub)
))
.fetch();
}
이 예제에서는 메인쿼리에와 서브쿼리가 모두 member를 조회하기 때문에 memberSub로 별칭을 직접 지정해줬다.
3. 서브쿼리 사용
1) SELECT 절에 서브쿼리 사용
요구사항 : 각 회원의 ID, 이름과 함께 해당 회원이 가장 최근 작성한 게시물 제목을 조회하라.
//member, post, JPAExpressions는 static import
List<Tuple> result = queryFactory
.select(
member.id,
member.name,
select(post.title)
.from(post)
.where(post.member.id.eq(member.id))
.orderBy(post.createdAt.desc())
.limit(1)
)
.from(member)
.fetch();
다른 테이블의 특정 데이터를 함께 조회해야할 때 SELECT절에 서브쿼리를 사용하는 것이 유용하다.
2) WHERE 절에 서브쿼리 사용
요구사항 : 가장 높은 주문 금액이 1000 이상인 회원을 조회하라.
//member, orders, JPAExpressions는 static import했다고 가정
List<Member> result = queryFactory
.selectFrom(member)
.where(
select(orders.amount.max())
.from(orders)
.where(orders.member.id.eq(member.id))
.gt(1000)
)
.fetch();
집계 함수 결과에 따라 필터링 해야 하는 경우, 서브쿼리를 사용할 수 있다.
4. FROM 절의 서브쿼리 해결방안
Querydsl은 FROM절의 서브쿼리를 지원하지 않는다.
1) 서브쿼리를 join으로 변경한다. (가능한 상황도 있고, 불가능한 상황도 있다.)
요구사항: Orders 테이블에 amount가 100이상인 주문을 한 회원을 조회하라.
✅FROM절 서브쿼리(SQL)
SELECT m.*
FROM Member m
JOIN (
SELECT o.member_id
FROM Orders o
WHERE o.amount >= 100
) sub ON m.id = sub.member_id;
✅조인으로 변경
List<Member> result = queryFactory
.selectFrom(member)
.join(member.orders, orders)
.where(orders.amount.goe(100))
.fetch();
2) 애플리케이션에서 쿼리를 2번 분리해서 실행한다.
요구사항: 회원의 총 주문 금액이 1000이상인 회원의 정보를 조회 하라.
✅FROM절 서브쿼리(SQL)
SELECT m.*
FROM Member m
JOIN (
SELECT o.member_id, SUM(o.amount) as total_amount
FROM Orders o
GROUP BY o.member_id
HAVING SUM(o.amount) >= 1000
)sub ON m.id = sub.member_id;
이 쿼리는 조인으로 해결할 수 있지만 예시로 쿼리를 분리해본다.
✅쿼리 분리
List<Long> memberIds = queryFactory
.select(orders.memberId)
.from(orders)
.groupBy(orders.memberId)
.having(orders.amount.sum().goe(1000))
.fetch();
List<Member> members = queryFactory
.selectFrom(member)
.where(member.id.in(memberIds))
.fetch();
첫번째 쿼리로 조건에 맞는 회원ID를 조회하고 두번째 쿼리로 회원ID와 일치하는 회원 정보를 조회한다.
💡조인이나 쿼리를 분리하는 것만으로 해결이 안되는 FROM절은 nativeSQL로 해결해야한다.
nativeSQL을 쓰기전에 데이터만 간단하게 가져오고 애플리케이션에서 로직처리를 할 수 있는지 확인하자.
복잡한 SQL대신 로직으로 해결할 수 있는 경우도 있다.
'인프런 김영한 강의 정리 > 실전! Querydsl' 카테고리의 다른 글
[Querydsl] 기본 문법(4) - Case문 (0) | 2024.08.09 |
---|---|
[Querydsl] 기본 문법(2) - 조인 (0) | 2024.08.08 |
[Querydsl] fetchResults(), fetchCount() 대체하기 (0) | 2024.08.07 |
[Querydsl] 기본 문법(1) - JPQL과 차이, 조회 (0) | 2024.08.06 |
프로젝트 환경설정 (SpringBoot 3.x 버전), Querydsl 설정 (0) | 2024.07.27 |