-
카테고리
-
세부 분야
백엔드
-
해결 여부
해결됨
이중 컬렉션(?)은 한 번에 조회가 불가능 할까요?
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
김영한
지식공유자2022.01.12
안녕하세요. 만두님
컬렉션은 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;
}
답변 1