• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

컬렉션 페치조인 batch size 활용 후 두개이상의 depth의 toOne 관계 lazy loading 시 join 이나 한방쿼리 활용방법 질문

22.07.12 14:01 작성 조회수 349

1

안녕하세요!

개인 프로젝트 중 만난 상황에 대해 질문이 있습니다!

 

컬렉션 페치조인, 페이징 처리시, batch size 를 활용하여 최적화를 진행 중입니다.

위와 같은 erd 에서 아티클을 페이징 조회 하면서 프로필, 태그 목록 까지 조회하고 싶습니다.

이때 아티클당 작성자의 프로필은 toOne 관계 이기 때문에 fetch join을 활용 하여 진행할 수 있었습니다.

 

이후에 프록시로 조회된 아티클 태그를 batch Size 를 이용해 n+1 문제 없이 조회하고

아티클 태그 에서는 태그를 조회해서 아티클->아티클태그->태그의 연관을 땡겨 올 수 있습니다.

결국 총 3회의 쿼리가 발생하는데 아티클 태그, 태그를 조회하는 추가 쿼리를 join 이나 where 절을 사용해 한방 쿼리로 묶을 수 있는 방법이 있는지 궁금합니다.

 

@Override
    public List<ArticleJpaEntity> searchArticle(long offset, long limit) {

       //쿼리 1 
        List<ArticleJpaEntity> articles = query
                .selectFrom(articleJpaEntity)
                .distinct()
                .join(articleJpaEntity.authorProfile, profileJpaEntity).fetchJoin()
                .offset(offset)
                .limit(limit)
                .fetch();

        //아티클 -> 아티클 태그 -> 태그의 lazy loading : 쿼리 2, 쿼리 3
        /*
              ArticlJpaEntity 의 메서드
              public List<String> getTagList() {

                  //쿼리 2: 아티클 -> 아티클 태그 레이지 로딩 발생 
                  return articleTags.stream()
                        //쿼리 3 : 아티클 태그 -> 태그의 레이지 로딩 발생
                        .map(ArticleTagJpaEntity::getTagName).collect(toList());
              }
         */

       //조회된 아티클을 돌며 수동 레이지 로딩 - batch size 로 n+1 방지
        for (ArticleJpaEntity article : articles) {
            article.getTagList();
        }

        return articles;
    }

 

querydsl 을 활용하였고 활용한 코드는 위와 같습니다.

 

발생한 쿼리를 요약하면 아래와 같습니다.

쿼리 1

select 
  distinct a.*, 
  p.* 
from 
  article a 
  inner join profile p on a.author_profile_id = p.id 
limit 
  20

쿼리 2

select 
  at.* 
from 
  article_tag at 
where 
  at.article_id in (7, 8, 9)

 

쿼리 3

select 
  t.* 
from 
  tag t 
where 
  t.id in (16, 17, 18)

 

참고로 전체 데이터는 아래와 같습니다.

insert into profile (bio, image, user_id, username, id) values ('user1bio', 'user1image', 1, 'user1', 4);
insert into profile (bio, image, user_id, username, id) values ('user2bio', 'user2image', 2, 'user2', 5);
insert into profile (bio, image, user_id, username, id) values ('user3bio', 'user3image', 3, 'user3', 6);

insert into article (author_profile_id, body, created_at, description, slug, title, updated_at, id)
values (4, 'article7body', '2022-07-11 22:42:43', 'article7desc', 'article7slug', 'article7title', NULL, 7);

insert into article (author_profile_id, body, created_at, description, slug, title, updated_at, id)
values (5, 'article8body', '2022-07-11 22:42:43', 'article8desc', 'article8slug', 'article8title', NULL, 8);

insert into article (author_profile_id, body, created_at, description, slug, title, updated_at, id)
values (6, 'article9body', '2022-07-11 22:42:43', 'article9desc', 'article9slug', 'article9title', NULL, 9);

-- 태그 16, 17, 18
insert into tag (name, id) values ('tag16', 16);
insert into tag (name, id) values ('tag17', 17);
insert into tag (name, id) values ('tag18', 18);


-- 아티클 7 -> 아티클 태그 19 -> 태그 16 - tag16

-- 아티클 8 -> 아티클 태그 20 -> 태그 16 - tag16
-- 아티클 8 -> 아티클 태그 21 -> 태그 17 - tag17

-- 아티클 9 -> 아티클 태그 22 -> 태그 16 - tag16
-- 아티클 9 -> 아티클 태그 23 -> 태그 17 - tag17
-- 아티클 9 -> 아티클 태그 24 -> 태그 18 - tag18
insert into article_tag (article_id, tag_id, id) values (7, 16, 19);

insert into article_tag (article_id, tag_id, id) values (8, 16, 20);
insert into article_tag (article_id, tag_id, id) values (8, 17, 21);

insert into article_tag (article_id, tag_id, id) values (9, 16, 22);
insert into article_tag (article_id, tag_id, id) values (9, 17, 23);
insert into article_tag (article_id, tag_id, id) values (9, 18, 24);

 

===

요약 - 쿼리 2와 쿼리 3을 한방 처리 할 수 있나요??

답변 1

답변을 작성해보세요.

1

안녕하세요. 득윤님

정리하자면 시작점을 기준으로 toOne으로 연결되는 부분은 모두 한방 쿼리가 가능하고, 중간에 컬렉션이 나오게 되면 그 부분 부터는 어렵습니다.

활용2편에서 설명드린 내용이어서, 자세한 내용은 활용2편 강의를 참고해주세요.

감사합니다.