-
QueryDSL 서브 쿼리 사용법Java/JPA 2019. 7. 14. 17:44반응형
QueryDSL 에서는 서브 쿼리도 작성이 가능합니다. 다만 모든 절에서 지원하는 것은 아니고, select 절과 where 절에서만 지원합니다.
먼저 select 절에서의 서브 쿼리를 보겠습니다.
@Data @NoArgsConstructor @AllArgsConstructor public class MemberCountDTO { private String teamName; private Long memberCount; }
우선 쿼리의 결과로 팀의 이름과, 팀에 속한 멤버들의 수를 담는 DTO 클래스입니다.
SELECT 절에서의 SubQuery
@Test public void simpleSubQueryTest() { QMember m = QMember.member; QTeam t = QTeam.team; query.select(Projections.fields(MemberCountDTO.class, t.teamName, ExpressionUtils.as( JPAExpressions.select(m.team.count()) .from(m) .where(m.team.eq(t)), "memberCount") )) .from(t) .fetch() .stream() .forEach(result -> { log.info("team name is : " + result.getTeamName()); log.info("member count is : " + result.getMemberCount()); }); }
제가 참고하면서 공부하고 있는 < 자바 ORM 표준 JPA 프로그래밍 (김영한 저) > 책에서는 JPASubQuery 객체를 이용하여 서브쿼리를 작성하는 예제가 나와있습니다. 하지만 현재 버전이 올라가면서 JPASubQuery 객체는 이용하지 않고, 위의 예제 코드처럼 JPAExpressions 객체를 이용하여 서브쿼리를 생성합니다.
ExpressionUtils 는 QueryDSL 내부에서 새로운 Expression 를 생성할 수 있도록 도와줍니다. 두 번째 인자로는 alias를 지정하여 memberCount 프로퍼티가 MemberCountDTO 객체에 잘 주입될 수 있도록 해줍니다.
쿼리의 결과로 위와 같이 SQL 이 작성되었습니다.
WHERE 절에서의 서브쿼리
@Test public void whereSubQueryTest() { QMember m = QMember.member; QTeam t = QTeam.team; query.selectFrom(t) .where(t.teamId.in( JPAExpressions .select(m.team.teamId) .from(m) .where(m.memberId.eq(1L)) )) .fetch() .stream() .forEach(result -> { log.info("team id is " + result.getTeamId()); log.info("team name is " + result.getTeamName()); }); }
위의 예제는 Member 의 id 가 1인 멤버가 속한 팀을 조회하는 쿼리입니다. where 절에서는 Expression을 사용하지 않으므로 ExpressionUtils 클래스를 사용하지 않아도 됩니다. 위의 쿼리는 아래와 같은 SQL 문으로 실행되는 것을 확인할 수 있습니다.
서브쿼리는 사실 성능적인 측면에서는 그다지 좋지 못합니다. 물론 서브쿼리를 사용하는 편이 더 나은 경우도 있습니다. 하지만 대부분의 경우에는 서브쿼리를 지양하는 것이 좋다고 생각합니다.
서브쿼리의 문제점은 다음과 같습니다.
- 서브쿼리는 테이블과 다르게 SQL 구문이 실행될 때에만 존재하는 비영속적인 임시 테이블입니다. 이 말은 곧, 서브쿼리에 접근할 때마다 SELECT 구문을 실행하여 데이터를 만들어야한다는 뜻입니다. 따라서 SELECT 구문을 실행하는 비용이 추가적으로 발생하게 됩니다.
- 서브쿼리는 비영속적인 임시 테이블이므로 실행된 후에 결과를 저장해두기 위해 메모리에 적재되어야 합니다. 메모리 용량이 충분하다면 이러한 오버헤드가 적지만, 데이터양이 큰 경우에는 DBMS가 저장소에 있는 파일에 결과를 쓸 때도 있습니다. 이러한 문제는 저장소의 성능에 따라 접근 속도가 급격하게 떨어지게 됩니다.
- 명시적인 제약과 인덱스가 작성되어 있는 테이블과 달리, 서브쿼리는 이러한 메타정보가 존재하지 않으므로 옵티마이저의 최적화를 받을 수 없습니다.
위와 같은 문제로 서브쿼리가 필요하다면 아래와 같은 방법을 고려하는 것이 좋아보입니다.
(출처 : https://jojoldu.tistory.com/379?category=637935)
- 1. Join을 이용하여 해결하는 방법
- 2. 어플리케이션 레벨에서 처리하는 방법
- 3. 쿼리를 나누어서 실행하는 방법
참고자료
https://jojoldu.tistory.com/379?category=637935
김영한님의 저서 < 자바 ORM 표준 JPA 프로그래밍 >
한빛미디어 출판 < SQL 레벨업 >
반응형'Java > JPA' 카테고리의 다른 글
[JPA] Auditing 설정 방법 (0) 2019.08.15 [QueryDSL] select 절에서 조회 대상 지정 방법 (0) 2019.07.13 JPA QueryDSL 시작해보기 (0) 2019.07.11