작성
·
48
0
안녕하세요. jpql을 다양하게 짜면서 나가는 sql이 어떻게 되는 지 학습하는 과정에서 질문이 생겨 글 남기게 됐습니다.
List<Member> resultList = em.createQuery("select m from Team t join t.members m", Member.class)
.getResultList();
for (Member member : resultList) {
System.out.println("member.getTeam() = " + member.getTeam().getClass());
}
위와 같이 jpql을 짜면 fetch join이 아니라 일반 join임에도 불구하고 조회한 member들에 대해서 member.getTeam().getName()을 해도 추가 쿼리가 나가지 않습니다. 위의 코드처럼 member.getTeam().getClass()를 통해 확인해보니 아래처럼 결과가 나와 member의 team이 프록시객체가 아니라 실제객체라는 것을 확인할 수 있었습니다.
Hibernate:
/* select
m
from
Team t
join
t.members m */ select
m1_0.id,
m1_0.age,
t1_0.id,
t1_0.name,
m1_0.username
from
Team t1_0
join
Member m1_0
on t1_0.id=m1_0.TEAM_ID
member.getTeam() = class jpql.Team
member.getTeam() = class jpql.Team
member.getTeam() = class jpql.Team
fetchType을 분명히 lazy로 했음에도 불구하고 위의 결과처럼 member만을 조회했는데도 불구하고 team까지 조회하는 sql이 나가는 이유가 무엇인가요?
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
문예찬님, JPQL에서 일반 조인을 사용했음에도 불구하고 추가적인 SQL 쿼리가 나가지 않는 이유는 JPA의 엔티티 그래프를 로딩하는 방식에서 찾을 수 있습니다.
JPA에서는 엔티티를 조회할 때, 기본적으로 Eager Fetch인 경우에는 즉시 로딩하지만, Lazy Fetch인 경우에는 실제로 그 관계의 속성에 접근할 때 쿼리를 실행하여 데이터를 로딩합니다. 그러나 JPQL 쿼리에서 관련 엔티티가 함께 조인되어 조회될 때는, 이미 그 데이터가 사용 가능한 상태로 조회되며, 이 경우에는 추가적인 지연 로딩 쿼리가 필요 없이 메모리에 로드된 상태로 사용됩니다.
즉, JPQL 자체에서 일반 조인을 사용할 경우에도, 관련된 엔티티가 Eager 로딩으로 간주되어 추가 쿼리 없이 해당 엔티티들이 메모리에 로드됩니다. 따라서 member.getTeam()
을 호출할 때 실제 객체가 반환되는 것이며, 이로 인해 프록시 객체가 아닌 실제 Team 객체의 클래스가 출력된 것입니다.
이 동작은 JPQL의 조인 최적화와 관련된 것으로, 기본적으로 세심한 엔티티 로딩을 조절하고 싶다면 @EntityGraph 등의 방법을 활용해 로딩 방식을 명시적으로 설정할 수 있습니다.
추가적인 정보는 스프링 데이터 JPA의 모델링 및 로딩 전략에서 얻으실 수 있습니다.
도움이 되었길 바랍니다. 좋은 학습 되세요!