[Spring][JPA] Hibernate에서 FROM 절 서브쿼리를 만들 수 없는 이유
Hibernate에서 FROM 절 서브쿼리를 만들 수 없는 이유
"어? SQL에서는 FROM 절 안에 서브쿼리를 넣을 수 있는데, Hibernate는 왜 안 될까?"
JPA를 처음 배우다 보면 누구나 한 번쯤 이런 의문을 품게 됩니다.
오늘은 이 질문을 정확하게, 그리고 실전 관점에서 풀어보겠습니다.
JPA와 Hibernate, 그리고 JPQL
우선 출발점부터 바로잡아야 합니다.
Hibernate는 JPA의 대표적인 구현체입니다.
그리고 JPA에서는 데이터베이스를 직접 다루지 않고,
JPQL이라는 "객체지향 쿼리 언어"를 사용합니다.
- SQL은 테이블을 대상으로 쿼리합니다.
- JPQL은 엔티티 객체를 대상으로 쿼리합니다.
이게 둘의 본질적인 차이입니다.
JPQL은 FROM 절 서브쿼리를 허용하지 않는다
문제는 여기서 발생합니다.
JPQL은 설계 철학상,
"객체(Entity)"를 중심으로 쿼리를 작성해야 한다는 원칙을 가지고 있습니다.
따라서
SELECT m FROM (SELECT m FROM Member m WHERE m.age > 20) m
같은 형태,
즉 FROM 절에 또 다른 SELECT 쿼리를 넣는 것을 지원하지 않습니다.
이유는 간단합니다:
JPQL의 FROM 절에는 반드시 "엔티티"만 등장해야 한다는 규칙이 있기 때문입니다.
JPQL은 "객체 간 탐색"에 초점을 맞췄지,
"임시 테이블"을 만드는 SQL 스타일 서브쿼리는 고려하지 않았습니다.
Hibernate는 표준을 따른다
Hibernate는 JPA를 구현할 때,
JPQL 표준 스펙을 그대로 따릅니다.
- JPQL이 허용하는 것만 Hibernate도 허용합니다.
- JPQL이 막아놓은 건 Hibernate도 막을 수밖에 없습니다.
결국 Hibernate가 FROM 절 서브쿼리를 막는 이유는,
Hibernate 자체의 한계가 아니라, JPA 스펙(JPQL) 자체의 제한입니다.
그렇다면 복잡한 쿼리는 어떻게 해야 할까?
실전에서는 FROM 절에 서브쿼리가 필요한 경우가 분명 존재합니다.
예를 들어,
- 통계 데이터
- 집계 결과를 다시 조인하는 경우
- 복잡한 필터링이 필요한 경우 등
이럴 때 실무에서는 보통 이렇게 해결합니다:
방법 설명
쿼리를 2단계로 나눈다 | 1차 조회 → 결과값으로 다시 조회 |
조인으로 대체 | 가능하면 서브쿼리 대신 조인 활용 |
Native Query 사용 | 직접 SQL 작성해서 사용하는 방법 |
QueryDSL 활용 | 복잡한 조건을 코드 조립 방식으로 작성 (단 FROM 서브쿼리는 불가) |
가장 간단한 방법은 Native Query를 쓰는 것입니다.
Spring Data JPA에서는 이렇게 작성할 수 있습니다:
@Query(value = "SELECT * FROM (SELECT * FROM member WHERE age > 20) m", nativeQuery = true)
List<Member> findMembersUsingSubquery();
물론 Native Query는 타입 안전성이 없고, 유지보수 비용이 올라가기 때문에 신중하게 써야 합니다.
요약
- Hibernate가 FROM 절 서브쿼리를 못 쓰는 건 Hibernate 한계가 아니다.
- JPQL(그리고 JPA 스펙) 자체가 FROM 절 서브쿼리를 막아두었기 때문이다.
- 복잡한 쿼리가 필요하면 쿼리 분리, 조인 대체, Native Query 등을 활용해야 한다.
✨ 마무리: Hibernate를 오해하지 말자
Hibernate는 단순히 표준을 따르고 있을 뿐입니다.
오히려 Hibernate는 JPQL을 SQL로 번역할 때 정말 많은 최적화를 수행합니다.
진짜 원인은 Hibernate가 아니라,
"JPQL은 객체 모델 탐색을 최우선으로 설계되었다"는 점을 이해하면 이 의문은 자연스럽게 풀립니다.
🔥 한줄로 정리
"Hibernate는 FROM 절 서브쿼리를 막은 게 아니라, JPA 표준(JPQL)이 그것을 막았을 뿐이다."