jpa활용2 강의를 듣고 있는데 김영한 강사님이 강의 중에 이런 말을 하셨음.

"fetch조인에서는 엘리어스를 쓰면 안된다.. .. .. "

그래서 왜지? 하고 생각만 했다가 완강하고 다시 검색해보는데.

 

https://www.inflearn.com/community/questions/15876/fetch-join-%EC%8B%9C-%EB%B3%84%EC%B9%AD%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8%EC%9E%85%EB%8B%88%EB%8B%A4

 

역시 나와 같은 사람이 있었음 ㅋ

그래서 답변을 쭉쭉 읽는데도 이해가 안 되어서 왜 안되나 했는데

ㅋㅋ..

 

내가 fetch 조인을 잘 모르고 있었음 ㅋ

그냥 단순히 fetch 조인 쓰면 연관관계에 있는 거 다 가져오는 거구만 개꿀 ㅋ

이 정도로만 생각해서 이해를 못 했던 것임

 

 

"JPA의 엔티티 객체 그래프는 DB와 데이터 일관성을 유지해야 하기 때문입니다.."

왜 사용하면 안되는지에 대한 영한님의 답변인데

<- 왜? 이것 또한 이해 못 했음..

그냥 가져오는 건 상관없는 거 아닌가 쩝 ..

이 생각했는데 fetch 조인은 엔티티를 가져오는 거 기 때문에.. 안됨. 큰일 남;;

이건 다음에 다른 글로 포스팅하고 먼저 이해해야 하는 일반 조인과 fatch 조인의 차이를 알아보겠다!

 


Fetch Join

일단 fetch 조인이란 sql과 비슷해 보이지만 jpql에서만 제공하는 기능으로 엔티티를 조회해 올 때 연관된 엔티티를

한 번에 같이 조회 할수 있는 기능이다. 

연관된 엔티티를 한번에 같이 조회할 수 있는 게 어떤 의미인가 하면! 함께 조회한 엔티티가 영속성 컨텍스트에 올라가게

되는데 밑에 예제를 보면서 자세히 보겠습니다 ㅋ

 

 

모든 멤버와 해당 멤버의 팀까지 조회해보겠습니다.

현재 데이터는 아래와 같습니다. 유저 1,2가 있고 모두 팀이 1인 상황입니다.

멤버, 팀 데이터

JPQL

// 1.일반조인
  public List<Member> findAll(){
        return em.createQuery("select m from Member m join m.team t",Member.class)
            .getResultList();
    }

// 2.fetch조인
    public List<Member> findAllWithFetchJoin(){
        return em.createQuery("select m from Member m join fetch m.team ",Member.class)
            .getResultList();
    }

 

일반 조인 테스트 코드

@Test
    public void 일반조인() throws Exception{
        List<Member> members  = memberRepository.findAll(); // 1
        for (Member member:members) {
            System.out.println("member.class = " + member.getTeam().getClass());
            System.out.println("member.class init= " + Hibernate.isInitialized(member.getTeam()));
            System.out.println("member name = " + member.getTeam().getName());
            member.getTeam().getName();
        }
    }

 

혹시 쿼리가 몇 번 나올 것인지 예상되시나요? 

처음 모든 Member를 가져오는 쿼리 한번 , 해당 멤버의 팀을 가져오는 쿼리 1번(멤버의 팀이 달랐다면 2번 쿼리가 나갔을 텐데 첫 번째 유저의 팀을 조회했을 때 영속성 컨텍스트에 저장되어 있기 때문에 1차 캐시에서 가져오고 DB조회 안 함)

도합 2번 나갑니다.

 

조회된 일반 조인 SQL

    select
        member0_.member_id as member_i1_4_,
        member0_.city as city2_4_,
        member0_.street as street3_4_,
        member0_.zipcode as zipcode4_4_,
        member0_.name as name5_4_,
        member0_.team_team_id as team_tea6_4_ 
    from
        member member0_ 
    inner join
        team team1_ 
            on member0_.team_team_id=team1_.team_id
member.class = class jpabook.jpashop.domain.Team$HibernateProxy$cRvKZ9yd -- 프록시객체
member.class init= false
2025-05-12 15:02:34.096 DEBUG 2092 --- [    Test worker] org.hibernate.SQL                        : 
    select
        team0_.team_id as team_id1_7_0_,
        team0_.name as name2_7_0_ 
    from
        team team0_ 
    where
        team0_.team_id=?
member name = Team1
member.class = class jpabook.jpashop.domain.Team$HibernateProxy$cRvKZ9yd-- 프록시객체
member.class init= true
member name = Team1

 

로그를 보면 member에서 team 객체에서 프록시 객체를 넣어 지연로딩 합니다.

그리고 나는 프록시 객체에서 초기화하면 실제 객체로 바뀌지 않을까? 했는데 그냥 초기화 해주고 로그를 보면 프록시 객체 그대로 쓰는 것 같다.

 

그러면 fetch 조인은 어떻게 될까요?

 

Fetch조인 테스트 코드

    @Test
    public void 페치조인() throws Exception{
        List<Member> membersFetch  = memberRepository.findAllWithFetchJoin(); // 1

        for (Member member:membersFetch) {
            System.out.println("membersFetch = " + member.getTeam().getName());
            member.getTeam().getName();
        }
    }

또 예상해 보시죠 ㅋ

쿼리가 몇 번 나갈까요?

한번 나갑니다.

fetch조인은 연관된 엔티티도 다 가져와 영속성 컨텍스트에 저장하기 때문에 연관객체를 프록시 객체로 가져오지 않고 실제 객체로 가져옵니다.

그래서 밑에서 team을 조회해도 다시 db에서 조회하지 않고 영속성 컨텍스트에 있는 값을 가져옵니다.

 

조회된 Fetch 조인 SQL

select
        member0_.member_id as member_i1_4_0_,
        team1_.team_id as team_id1_7_1_,
        member0_.city as city2_4_0_,
        member0_.street as street3_4_0_,
        member0_.zipcode as zipcode4_4_0_,
        member0_.name as name5_4_0_,
        member0_.team_team_id as team_tea6_4_0_,
        team1_.name as name2_7_1_ 
    from
        member member0_ 
    inner join
        team team1_ 
            on member0_.team_team_id=team1_.team_id
member.class = class jpabook.jpashop.domain.Team
membersFetch = Team1
member.class = class jpabook.jpashop.domain.Team
membersFetch = Team1

로그를 보면 실제객체를 참조하고 있는 게 보인다 ㅋ

 

정리해 보면 일반 조인은 값을 가져오긴 하지만 연관된 객체는 프록시 객체로 가져오고

fetch조인은 연관된 객체 모두 실제 객체로 초기화하여 영속성 컨텍스트에 저장한다!

 

 

여기까지만 알아보자 ㅋ

틀린 내용이 있다면 댓글 부탁드립니다 ㅋ

반응형

+ Recent posts