앞서서, 서브쿼리 사용시 생각해야할 점
현재 from절에서 서브쿼리가 불가능하다.
원인
JPA JPQL에서 from 절의 서브쿼리를 지원하지 않기 때문에, JPQL 기반의 QueryDsl도 지원되지 않는다.
하이버네이트 구현체를 사용하면(
JPAExpressions
) select절의 서비 쿼리는 지원한다.from절의 서브쿼리 해결 방안 3가지로는
- 서브쿼리를 join으로 변경 시도
- app에서 쿼리를 분리해서 2번 실행하여 거름
- nativeSQL을 사용하기
💢그러나 from절에서 서브쿼리를 사용하는 많은 이유 두 가지는
- 화면에 완전 Fit하게 가져오기 위해
- 성능상의 이유로 단 한번의 query만 날려 가져오게 하기 위해
정도로 구분할 수 있는데, 과연
DB에서 순수하게 Data를 가져오는 역할을 시켜서 재사용성을 높히는 설계
와,걸러내는 로직을 Server에서 한다
를 포기할 만한 가치가 있는가 생각해보자.또한, from 서브쿼리로 하나에 1000줄 짤거, sql을 두 세번 날리면 각각 100줄정도로 나눌 수 있는데
그렇게까지 쿼리 한두번이 아쉬울 정도의 고성능을 요구하려면 이미 cache나 다른 조치를 취해야 하는게 맞다.
JPAExpressions를 사용해 서브쿼리 제작하기
- where절에 서브쿼리예시 1. where 절 innerjoin으로 나이 가장 많은 회원 조회하기
- 💫주의사항 : 서브쿼리용 Entity는 alias가 달라야하기 때문에 따로 QMember 생성해준다.
JPAExpressions를 static import로 빼면 코드가 더 간결해진다.
@Test
public void subQuery() throws Exception {
*****
//Member InnerJoin을 위해 alias를 새로 선언해서 QMember 생성해주는 모습
*****
QMember subMember = new QMember("subMember");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(
JPAExpressions
.select(subMember.age.max())
.from(subMember)
)).fetch();
assertThat(result).extracting("age");
/* select
member1
from
Member member1
where
member1.age = (
select
max(subMember.age)
from
Member subMember
) */
/*select
member0_.user_id as user_id1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
where
member0_.age=(
select
max(member1_.age)
from
member member1_
)*/
}
예시 2. 나이 평균이상 멤버만 구하기
@Test
public void subQuery_avg() throws Exception{
//서브쿼리용 Entity는 alias가 달라야하기 때문에 따로 QMember 생성해준다.
QMember subMember=new QMember("subMember");
List<Member> result=queryFactory
.selectFrom(member)
.where(member.age.goe(
JPAExpressions
.select(subMember.age.avg())
.from(subMember)
)).fetch();
}
예시 3. 💫유용한 서브쿼리로 & in으로 조회
@Test
public void subQuery_in() throws Exception {
//서브쿼리용 Entity는 alias가 달라야하기 때문에 따로 QMember 생성해준다.
QMember subMember = new QMember("subMember");
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.in(
JPAExpressions
.select(subMember.age)
.from(subMember)
.where(subMember.age.gt(10))
)).fetch();
assertThat(result).extracting("age");
/* select
member1
from
Member member1
where
member1.age in (
select
subMember.age
from
Member subMember
where
subMember.age > ?1
) */
/*select
member0_.user_id as user_id1_0_,
member0_.age as age2_0_,
member0_.team_id as team_id4_0_,
member0_.username as username3_0_
from
member member0_
where
member0_.age in (
select
member1_.age
from
member member1_
where
member1_.age>?
)*/
}
2.select에 서브쿼리
간단 예시 : 유저 이름과 평균 나이 함께 출력하기
@Test
public void subQuery_select() throws Exception {
QMember subMember = new QMember("subMember");
List<Tuple> result = queryFactory
.select(member.username,
JPAExpressions
.select(subMember.age.avg())
.from(subMember))
.from(member)
.fetch();
for (Tuple tuple : result) {
System.out.println(tuple);
}
/* select
member1.username,
(select
avg(subMember.age)
from
Member subMember)
from
Member member1 */
/*select
member0_.username as col_0_0_,
(select
avg(cast(member1_.age as double))
from
member member1_) as col_1_0_
from
member member0_*/
}
'스프링 > QueryDsl' 카테고리의 다른 글
[QueryDsl] Projection(select에서 뭘 가져올까?)종류에 따른 결과 반환 (Tuple, Dto로 반환)하는 법 (0) | 2022.03.11 |
---|---|
[QueryDsl] Case문, 상수 출력, 특정 문자값 붙여 출력하기 (0) | 2022.03.10 |
[QueryDsl] Join + On절 + Fetch join (0) | 2022.03.08 |
[QueryDsl] 정렬, 페이징, Tuple을 이용한 특정 값 Select (max,min,avg,sum 등) (0) | 2022.03.06 |
[Querydsl] 다양한 Fetch 결과 받아보기 (0) | 2022.03.05 |