• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

이중 컬렉션(?)은 한 번에 조회가 불가능 할까요?

22.01.09 19:56 작성 조회수 249

0

안녕하세요 영한님! 토이 프로젝트를 진행하면서 문제가 생겼는데, 해결이 안되어서 질문드리러 왔습니다😭

현재 인스타그램 클론 코딩을 하고 있는데요, 게시물을 업로드할 때, 이미지를 여러 장 추가할 수 있고, 각각 이미지에 다른 유저를 여러 명 태그를 할 수 있게 되어 있습니다.

구조를 보면, 게시물 안에 이미지 리스트가 있고, 각각 이미지 안에 태그 리스트가 존재하게 됩니다.

✅Q1. 이러한 상황에서 게시물 목록을 조회하려 할 때, 이미지와 태그도 한 번에 담아서 조회하는 방법이 있을까요?

queryFactory
.selectFrom(post)
.leftJoin(post.postImages, postImage).fetchJoin().distinct()
.leftJoin(postImage.postImageTags, postImageTag).fetchJoin().distinct()
.orderBy(post.id.desc())
.limit(11)
.fetch();

위의 사진과 같이 조회하면 cannot simultaneously fetch multiple bags 예외가 나오더라구요ㅠㅠ

✅Q2. 만약 위와 같이 한 번에 조회하는 게 불가능하다면, 다른 좋은 방법이 있는지 궁금합니다.

답변 1

답변을 작성해보세요.

1

안녕하세요. 만두님

컬렉션은 2번 이상 fetch join이 불가능합니다.

활용2편을 다시한번 복습하시면 답을 찾으실 수 있을거에요.

감사합니다.

만두님의 프로필

만두

질문자

2022.01.15

안녕하세요 영한님!
복습을 하고 나서 다음과 같이 구현을 해보았습니다.

1. 모든 Post를 조회하여 List<Post> result에 저장.
2. 위에서 조회한 Post의 id만 list로 따로 뽑아낸 다음, in절을 이용하여 해당 list에 담겨 있는 postId에 속한 모든 PostImage들을 조회
3. 방금 조회한 데이터들을 postId 기준으로 grouping하여 Map<Long, List<PostImage>> 형식으로 데이터를 변환
4. 변환한 데이터를 result에 적용

이런 방식으로 하면, 1 + 1번의 쿼리로 컬렉션 조회가 가능하다는 것을 이해했습니다.
여기서 더 나아가, 각각의 PostImage에도 List<PostImageTag>가 존재하는데, 위의 방식을 그대로 적용해보면, 다시 PostImage들의 id를 list로 뽑아낸 후에, in절을 이용하여 이에 속하는 모든 PostImageTag를 조회하고, 이를 다시 Map 형태로 변환한 이후 result에 적용하는 방식으로 구현을 하면, 최종적으로 1 + 1 + 1번의 쿼리로 원하는 데이터를 가져올 수 있더라구요!

답안지를 옆에 두고 답을 물어보고 있었다니.. ㅠ 바보같았네요.. 아무튼 정말 큰 도움이 되었습니다! 이번 기회에 영한님 로드맵 두 개 다 한 번씩 복습 돌려봐야겠네요! 항상 감사드립니다 :)

아래는 최종 코드입니다!

public List<PostDTO> findRecent10PostDTOs(Long memberId) {
final List<PostDTO> postDTOs = queryFactory
.select(new QPostDTO(
post.id,
post.content,
post.uploadDate,
post.member.username,
post.member.name,
post.member.image.imageUrl,
post.comments.size(),
post.postLikes.size(),
JPAExpressions
.selectFrom(bookmark)
.where(bookmark.post.eq(post).and(bookmark.member.id.eq(memberId)))
.exists(),
JPAExpressions
.selectFrom(postLike)
.where(postLike.post.eq(post).and(postLike.member.id.eq(memberId)))
.exists(),
JPAExpressions
.select(postLike.member.username)
.from(postLike)
.where(postLike.post.eq(post)
.and(postLike.member
.in(JPAExpressions
.select(follow.followMember)
.from(follow)
.where(follow.member.id.eq(memberId)))))
.limit(1)
))
.from(post)
.join(post.member, QMember.member)
.on(post.member.username.in(
JPAExpressions
.select(follow.followMember.username)
.from(follow)
.where(follow.member.id.eq(memberId))
))
.limit(10)
.orderBy(post.id.desc())
.fetch();

final List<Long> postIds = postDTOs.stream()
.map(PostDTO::getPostId)
.collect(Collectors.toList());

final List<PostImageDTO> postImageDTOs = queryFactory
.select(new QPostImageDTO(
postImage.post.id,
postImage.id,
postImage.image
))
.from(postImage)
.where(postImage.post.id.in(postIds))
.fetch();

final List<Long> postImageIds = postImageDTOs.stream()
.map(PostImageDTO::getId)
.collect(Collectors.toList());

final List<PostTagDTO> postTagDTOs = queryFactory
.select(new QPostTagDTO(
postTag.postImage.id,
postTag.id,
postTag.tag
))
.from(postTag)
.where(postTag.postImage.id.in(postImageIds))
.fetch();

final Map<Long, List<PostTagDTO>> postImageDTOMap = postTagDTOs.stream()
.collect(Collectors.groupingBy(PostTagDTO::getPostImageId));
postImageDTOs.forEach(i -> i.setPostTagDTOs(postImageDTOMap.get(i.getId())));

final Map<Long, List<PostImageDTO>> postDTOMap = postImageDTOs.stream()
.collect(Collectors.groupingBy(PostImageDTO::getPostId));
postDTOs.forEach(p -> p.setPostImageDTOs(postDTOMap.get(p.getPostId())));

return postDTOs;
}

스스로 잘 해결하셨습니다^^