카테고리 없음
JPA 페치 조인
머룽
2023. 4. 20. 09:52
저번 시간에 이어서 조인 중이 페치 조인에 대해서 알아보자.
저번에는 내부 외부 컬렉션 세타 조인에 대해서 알아봤다.
페치 조인은 우리가 흔히 쓰던 sql 조인이 아니고 JPQL에서 성능 최적화를 위해 제공되는 기능이다. 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능으로 fetch 명령어를 사용할 수 있다.
페치 조인 ::= [LEFT [OUTER] | INNER] JOIN FETCH 조인경로
위는 JPA 표준 명세에 정의된 페치 조인이다.
엔티티 페치 조인
페치 조인을 사용해 보자select m
from Member m join fetch m.team
연관된 엔티티와 컬렉션을 함께 조회하는데 여기서 회원(m)과 팀(m.team)을 함께 조회한다. 일반 JPQL과는 다르게 m.team 에는 별칭이 없는데 페치 조인에서는 별칭을 사용할 수 없다.
예제를 보자
private static void fetchJoin(EntityManager entityManager) {
String jpql = "select m from Member m join fetch m.team";
List<Member> members = entityManager.createQuery(jpql, Member.class)
.getResultList();
for(Member member : members){
//페치 조인으로 회원과 팀을 함께 조회해서 지연로딩 발생안함
System.out.println("username = " + member.getName() + ", teamname = " + member.getTeam().getName());
}
}
만약 회원과 팀을 지연로딩으로 설정했다고 가정하자. 회원을 조회할 때는 페치조인을 사용해서 팀도 함께 조회 했으므로 연관된 팀 엔티티는 프록시가 아닌 실제 엔티티를 가져온다. 이때 만약 준영속상태가 되어도 연관된 팀 엔티티를 조회 할 수 있다.
컬렉션 페치 조인
이번엔 위와 동일하지만 일대다 관계인 컬렉션을 페치 조인 해보자.select t
from Team t join fetch t.members
from t.name = team1
이렇게 페치 조인을 하면 연관된 member 컬렉션도 함께 조회된다.
private static void collectionFetchJoin(EntityManager entityManager) {
String jpql = "select t from Team t join fetch t.members where t.name = team1";
List<Team> teams = entityManager.createQuery(jpql, Team.class)
.getResultList();
for(Team team : teams){
System.out.println("teamname = " + team.getName());
//페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
for(Member member : team.getMembers()){
System.out.println(" ->username = " + member.getName()+ ", member = " + member);
}
}
}
이거 역시 페치 조인으로 지연로딩이 발생 하지 않는다.
하지만 여기서 문제는 팀은 하나지만 member 테이블과 조인하면서 결과가 증가했다. 그래서 team 엔티티에 2건이 조회 되었다.
페치 조인과 DISTINCT
SQL의 DISTINCT는 중복된 결과를 제거 하는 명령어다. JPQL의 DISTINCT 명령어는 SQL에 DISTINCT를 추가 하는 것은 물론이고 애플리케이션에서 한 번 더 중복을 제거한다.select distinct t
from Team t join fetch t.members
where t.name = team1
select distinct t 의 의미는 팀의 엔티티의 중복을 제거하는 것이다. 따라서 중복으로 조회 되었던 team1이 한건만 조회된다.
private static void collectionFetchJoinDistinct(EntityManager entityManager) {
String jpql = "select distinct t from Team t join fetch t.members where t.name = team1";
List<Team> teams = entityManager.createQuery(jpql, Team.class)
.getResultList();
for(Team team : teams){
System.out.println("teamname = " + team.getName());
//페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
for(Member member : team.getMembers()){
System.out.println(" ->username = " + member.getName()+ ", member = " + member);
}
}
}