끄적끄적

[SPRING + JPA] QueryDsl FetchJoin 본문

Back-end/Java

[SPRING + JPA] QueryDsl FetchJoin

mashko 2021. 11. 16. 03:02
반응형

최근 생각도 못했던 문제가 있어 또 한번 고생한 경험이 있다.
JPA를 쓰는 환경에서는 대체로 아래와 같이 릴레이션에 대한 조인을 걸어두고 쓰는 경우가 많을 것이다.

queryDsl로 리스트를 만들어주는 부분에서 조인걸린 테이블에 대한 조건처리를 해야할 경우가 생겼다.
왜 여태까지 이런 경우가 많이 없었는지 이해가 안되지만..
또 한번의 삽질이 시작되었다. queryDsl에 조인을 걸고 조인걸린 부분에 대한 조건을 추가하고 확인해본결과
이상한 데이터가 자꾸 포함되어 조회가 되는것이였다. 로그에 남겨진 쿼리를 모두 분석하기 시작했다..
쿼리가 나뉘어서 날라가면서 발생한 문제였다. 부모 테이블에서 조건처리에 대한 부분을 필터링 한 다음에 id를 기반으로 다시 IN을 하는것이다.
튜닝을 위해 찾아보기 시작했다.
fetchJoin.. 연관된 엔티티 or 컬렉션을 SQL 한번에 함께 조회하는 기능이 있다는 문구를 봤다.
그래서 아래와 같이 튜닝했다.

factory.selectFrom(exampleOrm)
                .join(exampleOrm.examplePeriodOrmList, examplePeriodOrm)
                .fetchJoin()
                .where(where)
                .orderBy(exampleOrm.createdAt.desc())
                .offset(offset)
                .limit(request.getSize())
                .distinct();

작성 후 런.. 로그를 보던중에 이상한걸 발견
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
!? 이게 뭐지 확인해보니
fetch join 과 pagination 을 같이 할 시
"모든 데이터"를 전부 가져와 메모리에서 걸러낸다는 것이다.
이건 좀 아니다 싶었다. 그래서 다시 리펙토링..
limit과 offset,orderby부분만 다르게 처리하면 되는 것 같았다.

List<Long> ids = factory.select(exampleOrm.id)
                .from(exampleOrm)
                .join(exampleOrm.examplePeriodOrmList, examplePeriodOrm)
                .fetchJoin()
                .where(where)
                .offset(offset)
                .limit(request.getSize())
                .fetch();

List<ExampleOrm> list = factory.selectFrom(exampleOrm)
                .join(exampleOrm.examplePeriodOrmList, examplePeriodOrm)
                .fetchJoin()
                .where(exampleOrm.id.in(ids))
                .where(where)
                .orderBy(exampleOrm.createdAt.desc())
                .fetch();


            // 총 카운트
            Long count = factory
                .select(Wildcard.count)
                .from(exampleOrm)
                .where(where)
                .fetch()
                .get(0);

            result = new QueryResults<PolicyOrm>(list, Long.valueOf(request.getSize()), Long.valueOf(offset), count);

위와같이 id 리스트를 먼저 얻은 뒤 이후에 in절로 필요한 부분만 처리한 후 런..해보니 없어졌다.
테스트코드를 돌려보니 잘된다. fetchJoin을 사용할 때 앞으로 조심해야 할 듯 하다.

반응형
Comments