-
[QueryDSL] QueryDSL에서 조인 시 주의사항 | 객체 그래프 탐색Spring 2023. 10. 22. 19:56
QueryDSL에서 groupBy를 사용하는 예제를 공부하던 중, '왜 이렇게 조인을 해야하는지'에 대한 의문을 가지게 되었다.
🔍 기본 조인 방식
먼저, 기본적인 조인 방식을 살펴보자.
List<Tuple> result = queryFactory .select(team.name, member.age.avg()) .from(member) .join(member.team, team) .groupBy(team.name) .fetch();
이 코드는 member 테이블을 기반으로 team 테이블과 조인하는 방식으로 작성되었다.
from 절에는 member를 시작점으로 설정하고, join에서는 member와 연관된 team을 찾기 위해 (member.team, team) 파라미터를 사용했다. 이러한 방식은 member가 team을 참조하는 외래키를 가지고 있기 때문에 가능한 방식이다.
JPQL의 특징 중 하나는 SQL과는 달리 객체 그래프를 탐색하는 방식으로 쿼리를 작성해야 한다는 것이다.
이때, from 절은 탐색의 시작점을 나타낸다.
🙅🏻♀️ 잘못된 조인 시도
하지만 다음과 같이 from 절에서 team을 시작점으로 선택하고, join에서 (member.team, team) 파라미터를 사용하면 쿼리 오류가 발생한다.
왜냐하면, JPQL에서는 from 절을 시작점으로 객체 그래프를 탐색하기 때문에 team부터 시작하여 조인을 진행해야 하는데 여기서의 조인은 member를 시작점으로 하고 있어서 잘못된 경로를 탐색하게 되어 오류가 발생한다.
따라서 조인을 사용할 때는 해당 테이블 간의 관계와 객체 그래프의 탐색 경로를 잘 이해하고, 올바른 시작점을 설정해야 한다 !
List<Tuple> result = queryFactory .select(team.name, member.age.avg()) .from(team) .join(member.team, team) .groupBy(team.name) .fetch();
java.lang.IllegalArgumentException: org.hibernate.query.sqm.InterpretationException: Error interpreting query [select team.name, avg(member1.age) from Team team inner join member1.team as team group by team.name]; this may indicate a semantic (user query) problem or a bug in the parser [select team.name, avg(member1.age) from Team team inner join member1.team as team group by team.name]
✅ 해결
아래는 이를 기반으로 수정된 코드이다. 🙂
List<Tuple> result = queryFactory .select(team.name, member.age.avg()) .from(team) .join(team.members, member) .groupBy(team.name) .fetch();
⭐️ 테이블 간의 관계와 JPQL의 특성을 항상 염두에 두고 쿼리를 작성하자 ! ⭐️
📋 참고 자료
728x90'Spring' 카테고리의 다른 글
[Spring] Kafka 채팅 기능 구현 - 1 (0) 2023.12.19 [Spring] @GeneratedValue | 엔티티의 기본 키 생성 전략 (0) 2023.06.18 [Spring] TDD와 단위 테스트 (0) 2023.06.18 [Spring] Spring Security + JWT 로그인 구현하기 - 4 (6) 2023.06.16 [Spring] Spring Security + JWT 로그인 구현하기 - 3 (0) 2023.06.15