[JPA] QueryDSL Fetch & FetchJoin

타미·2020년 10월 3일
5

JPA실험실

목록 보기
3/8
post-custom-banner

Fetch Join

JPA Lazy Loding N+1 문제를 방지하기 위해 사용된다.

        return jpaQueryFactory.selectFrom(instagram)
                .innerJoin(instagram.place, place)
                .fetchJoin()
                .fetch();
  • join식을 써주고, fetchJoin을 걸어준다.
  • 그러면 instagram과 place가 모두 조회된다.
  • fetchJoin을 안써주면 당연히 instagram 밖에 조회가 안된다.
  • join fetch는 JPQL에서 제공하는 것
    • (참고) N+1는 Entity Graph로도 해결 가능
      JoinFetch: Join 방식 선택 가능, Entity Graph: Outer Join
      Join 종류
Hibernate: 
    select
        instagram0_.instagram_id as instagra1_4_0_,
        place1_.place_id as place_id1_8_1_,
        instagram0_.created_date as created_2_4_0_,
        instagram0_.modified_date as modified3_4_0_,
        instagram0_.hashtag_count as hashtag_4_4_0_,
        instagram0_.hashtag_name as hashtag_5_4_0_,
        instagram0_.place_id as place_id6_4_0_,
        place1_.created_date as created_2_8_1_,
        place1_.modified_date as modified3_8_1_,
        place1_.category as category4_8_1_,
        place1_.kakao_id as kakao_id5_8_1_,
        place1_.latitude as latitude6_8_1_,
        place1_.longitude as longitud7_8_1_,
        place1_.road_address_name as road_add8_8_1_,
        place1_.place_name as place_na9_8_1_,
        place1_.place_url as place_u10_8_1_ 
    from
        instagram instagram0_ 
    inner join
        place place1_ 
            on instagram0_.place_id=place1_.place_id

Fetch & FetchJoin

내가 헷갈렸던 부분은

        return jpaQueryFactory.selectFrom(instagram)
                .innerJoin(instagram.place, place)
                .fetchJoin() // 여기서의 fetch 1
                .fetch(); // 또 여기서의 fetch 2

요렇게 fetch가 2번 나와서 헷갈렸다.

두번째 fetch는 결과 반환의 용도이다.

결과반환

  • fetch : 조회 대상이 여러건일 경우. 컬렉션 반환
  • fetchOne : 조회 대상이 1건일 경우(1건 이상일 경우 에러). generic에 지정한 타입으로 반환
  • fetchFirst : 조회 대상이 1건이든 1건 이상이든 무조건 1건만 반환. 내부에 보면 return limit(1).fetchOne() 으로 되어있음
  • fetchCount : 개수 조회. long 타입 반환
  • fetchResults : 조회한 리스트 + 전체 개수를 포함한 QueryResults 반환. count 쿼리가 추가로 실행된다.

Fetch Join 주의사항

갓졸두님 블로그를 보면서 한번도 생각하지 못한 것을 알게 되었다.

OneToMany 관계에서 findAllBatchJoin할 때 의도치 않게 동일한 One이 여러 개 반환될 수 있다는 것이다.

상황

class One {
    	...
        @OneToMany(fetchType=Lazy)
        @JoinColumn(name = "many_id")
        List<Many> manyList;
   }

One 안에 3개의 Many 있다.
이때, fetchJoin으로 데이터를 가져오면

    select
    	one, many
    from
        one 
    inner join
    	many
    on
    	one.id=many.id

요런 식의 Query가 생긴다.

그러면 3개의 row가 return되면서 One 3개가 생긴다!

    private void setUp() {
        One one = new One();
        List<Many> manyList = manyRepository.saveAll(Arrays.asList(new Many(), new Many(), new Many()));
        one.manyList = manies;
        oneRepository.save(one);
    }

    @Test
    void test() {
        setUp();
        List<One> ones = testRepository.findAllFetch();
        
        assertThat(ones).hasSize(3);
        assertThat(ones.get(0)).isSameAs(allFetch.get(1));
        assertThat(ones.get(0)).isSameAs(allFetch.get(2));
    }
  • ones의 원소는 모두 같은 주소값을 가진다.

해결방법

  1. List 대신 Set
  2. Distinct 넣어주기

이래서 OneToMany를 지양하나보다!

profile
IT's 호기심 천국
post-custom-banner

0개의 댓글