Skip to content

Commit

Permalink
채팅방 목록 조회 시 경매 이미지 개수만큼 데이터가 중복되는 현상 해결 및 쿼리 최적화 (#411)
Browse files Browse the repository at this point in the history
* fix: 이미지 개수만큼 채팅방이 중복 조회 되는 문제 해결

* rename: dto 이름 변경

* refactor: 채팅방 아이디로 채팅방 조회할 때 쿼리 개선

* style: 나중에 하기로 한 것 todo 추가
  • Loading branch information
kwonyj1022 authored and swonny committed Oct 6, 2023
1 parent 531ac1b commit 1e0c612
Show file tree
Hide file tree
Showing 22 changed files with 382 additions and 200 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import com.ddang.ddang.chat.application.exception.InvalidUserToChat;
import com.ddang.ddang.chat.domain.ChatRoom;
import com.ddang.ddang.chat.infrastructure.persistence.JpaChatRoomRepository;
import com.ddang.ddang.chat.infrastructure.persistence.QuerydslChatRoomAndMessageRepository;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageDto;
import com.ddang.ddang.chat.infrastructure.persistence.QuerydslChatRoomAndImageRepositoryImpl;
import com.ddang.ddang.chat.infrastructure.persistence.QuerydslChatRoomAndMessageAndImageRepository;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto;
import com.ddang.ddang.user.application.exception.UserNotFoundException;
import com.ddang.ddang.user.domain.User;
import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository;
Expand All @@ -34,7 +36,8 @@ public class ChatRoomService {
private static final Long DEFAULT_CHAT_ROOM_ID = null;

private final JpaChatRoomRepository chatRoomRepository;
private final QuerydslChatRoomAndMessageRepository querydslChatRoomAndMessageRepository;
private final QuerydslChatRoomAndImageRepositoryImpl querydslChatRoomAndImageRepository;
private final QuerydslChatRoomAndMessageAndImageRepository querydslChatRoomAndMessageAndImageRepository;
private final JpaUserRepository userRepository;
private final JpaAuctionRepository auctionRepository;

Expand Down Expand Up @@ -83,24 +86,25 @@ private boolean isNotSellerAndNotWinner(final User findUser, final Auction findA
public List<ReadChatRoomWithLastMessageDto> readAllByUserId(final Long userId) {
final User findUser = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다."));
final List<ChatRoomAndMessageDto> chatRoomAndMessageQueryProjectionDtos =
querydslChatRoomAndMessageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(findUser.getId());
final List<ChatRoomAndMessageAndImageDto> chatRoomAndMessageAndImageQueryProjectionDtos =
querydslChatRoomAndMessageAndImageRepository.findAllChatRoomInfoByUserIdOrderByLastMessage(findUser.getId());

return chatRoomAndMessageQueryProjectionDtos.stream()
.map(dto -> ReadChatRoomWithLastMessageDto.of(findUser, dto))
.toList();
return chatRoomAndMessageAndImageQueryProjectionDtos.stream()
.map(dto -> ReadChatRoomWithLastMessageDto.of(findUser, dto))
.toList();
}

public ReadParticipatingChatRoomDto readByChatRoomId(final Long chatRoomId, final Long userId) {
final User findUser = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException("사용자 정보를 찾을 수 없습니다."));
final ChatRoom chatRoom = chatRoomRepository.findChatRoomById(chatRoomId)
.orElseThrow(() -> new ChatRoomNotFoundException(
"지정한 아이디에 대한 채팅방을 찾을 수 없습니다."
));
checkAccessible(findUser, chatRoom);

return ReadParticipatingChatRoomDto.of(findUser, chatRoom, LocalDateTime.now());
final ChatRoomAndImageDto chatRoomAndImageDto =
querydslChatRoomAndImageRepository.findChatRoomById(chatRoomId)
.orElseThrow(() -> new ChatRoomNotFoundException(
"지정한 아이디에 대한 채팅방을 찾을 수 없습니다."
));
checkAccessible(findUser, chatRoomAndImageDto.chatRoom());

return ReadParticipatingChatRoomDto.of(findUser, chatRoomAndImageDto, LocalDateTime.now());
}

private void checkAccessible(final User findUser, final ChatRoom chatRoom) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.ddang.ddang.auction.domain.Auction;
import com.ddang.ddang.bid.domain.Bid;
import com.ddang.ddang.image.domain.AuctionImage;

public record ReadAuctionInChatRoomDto(
Long id,
Expand All @@ -10,12 +11,12 @@ public record ReadAuctionInChatRoomDto(
Long thumbnailImageId
) {

public static ReadAuctionInChatRoomDto from(final Auction auction) {
public static ReadAuctionInChatRoomDto of(final Auction auction, final AuctionImage thumbnailImage) {
return new ReadAuctionInChatRoomDto(
auction.getId(),
auction.getTitle(),
convertPrice(auction.getLastBid()),
auction.getAuctionImages().get(0).getId()
thumbnailImage.getId()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.ddang.ddang.chat.domain.ChatRoom;
import com.ddang.ddang.chat.domain.Message;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto;
import com.ddang.ddang.image.domain.AuctionImage;
import com.ddang.ddang.user.domain.User;

import java.time.LocalDateTime;
Expand All @@ -17,15 +18,16 @@ public record ReadChatRoomWithLastMessageDto(

public static ReadChatRoomWithLastMessageDto of(
final User findUser,
final ChatRoomAndMessageDto chatRoomAndMessageDto
final ChatRoomAndMessageAndImageDto chatRoomAndMessageAndImageDto
) {
final ChatRoom chatRoom = chatRoomAndMessageDto.chatRoom();
final ChatRoom chatRoom = chatRoomAndMessageAndImageDto.chatRoom();
final User partner = chatRoom.calculateChatPartnerOf(findUser);
final Message lastMessage = chatRoomAndMessageDto.message();
final Message lastMessage = chatRoomAndMessageAndImageDto.message();
final AuctionImage thumbnailImage = chatRoomAndMessageAndImageDto.thumbnailImage();

return new ReadChatRoomWithLastMessageDto(
chatRoom.getId(),
ReadAuctionInChatRoomDto.from(chatRoom.getAuction()),
ReadAuctionInChatRoomDto.of(chatRoom.getAuction(), thumbnailImage),
ReadUserInChatRoomDto.from(partner),
ReadLastMessageDto.from(lastMessage),
chatRoom.isChatAvailableTime(LocalDateTime.now())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.ddang.ddang.chat.application.dto;

import com.ddang.ddang.chat.domain.ChatRoom;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto;
import com.ddang.ddang.user.domain.User;

import java.time.LocalDateTime;
Expand All @@ -14,14 +15,15 @@ public record ReadParticipatingChatRoomDto(

public static ReadParticipatingChatRoomDto of(
final User findUser,
final ChatRoom chatRoom,
final ChatRoomAndImageDto chatRoomAndImageDto,
final LocalDateTime targetTime
) {
final ChatRoom chatRoom = chatRoomAndImageDto.chatRoom();
final User partner = chatRoom.calculateChatPartnerOf(findUser);

return new ReadParticipatingChatRoomDto(
chatRoom.getId(),
ReadAuctionInChatRoomDto.from(chatRoom.getAuction()),
ReadAuctionInChatRoomDto.of(chatRoom.getAuction(), chatRoomAndImageDto.thumbnailImage()),
ReadUserInChatRoomDto.from(partner),
chatRoom.isChatAvailableTime(targetTime)
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ddang.ddang.chat.infrastructure.persistence;

import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto;

import java.util.Optional;

public interface QuerydslChatRoomAndImageRepository {

Optional<ChatRoomAndImageDto> findChatRoomById(final Long chatRoomId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.ddang.ddang.chat.infrastructure.persistence;

import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndImageQueryProjectionDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndImageQueryProjectionDto;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.Optional;

import static com.ddang.ddang.auction.domain.QAuction.auction;
import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom;
import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage;

@Repository
@RequiredArgsConstructor
public class QuerydslChatRoomAndImageRepositoryImpl implements QuerydslChatRoomAndImageRepository {

private final JPAQueryFactory queryFactory;

@Override
public Optional<ChatRoomAndImageDto> findChatRoomById(final Long chatRoomId) {

final ChatRoomAndImageQueryProjectionDto chatRoomAndImageQueryProjectionDto =
queryFactory.select(new QChatRoomAndImageQueryProjectionDto(chatRoom, auctionImage))
.from(chatRoom)
.leftJoin(chatRoom.buyer).fetchJoin()
.leftJoin(chatRoom.auction, auction).fetchJoin()
.leftJoin(auction.seller).fetchJoin()
.leftJoin(auctionImage).on(auctionImage.id.eq(
JPAExpressions
.select(auctionImage.id.min())
.from(auctionImage)
.where(auctionImage.auction.id.eq(auction.id))
.groupBy(auctionImage.auction.id)
)).fetchJoin()
.leftJoin(auction.lastBid).fetchJoin()
.where(chatRoom.id.eq(chatRoomId))
.fetchOne();

if (chatRoomAndImageQueryProjectionDto == null) {
return Optional.empty();
}

return Optional.of(chatRoomAndImageQueryProjectionDto.toDto());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ddang.ddang.chat.infrastructure.persistence;

import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto;

import java.util.List;

public interface QuerydslChatRoomAndMessageAndImageRepository {

List<ChatRoomAndMessageAndImageDto> findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.ddang.ddang.chat.infrastructure.persistence;

import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.ChatRoomAndMessageAndImageQueryProjectionDto;
import com.ddang.ddang.chat.infrastructure.persistence.dto.QChatRoomAndMessageAndImageQueryProjectionDto;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

import static com.ddang.ddang.auction.domain.QAuction.auction;
import static com.ddang.ddang.chat.domain.QChatRoom.chatRoom;
import static com.ddang.ddang.chat.domain.QMessage.message;
import static com.ddang.ddang.image.domain.QAuctionImage.auctionImage;
import static java.util.Comparator.comparing;

@Repository
@RequiredArgsConstructor
public class QuerydslChatRoomAndMessageAndImageRepositoryImpl implements QuerydslChatRoomAndMessageAndImageRepository {

private final JPAQueryFactory queryFactory;

@Override
public List<ChatRoomAndMessageAndImageDto> findAllChatRoomInfoByUserIdOrderByLastMessage(final Long userId) {
final List<ChatRoomAndMessageAndImageQueryProjectionDto> unsortedDtos =
queryFactory.select(new QChatRoomAndMessageAndImageQueryProjectionDto(chatRoom, message, auctionImage))
.from(chatRoom)
.leftJoin(chatRoom.buyer).fetchJoin()
.leftJoin(chatRoom.auction, auction).fetchJoin()
.leftJoin(auction.seller).fetchJoin()
.leftJoin(auctionImage).on(auctionImage.id.eq(
JPAExpressions
.select(auctionImage.id.min())
.from(auctionImage)
.where(auctionImage.auction.id.eq(auction.id))
.groupBy(auctionImage.auction.id)
)).fetchJoin()
.leftJoin(auction.lastBid).fetchJoin()
.leftJoin(message).on(message.id.eq(
JPAExpressions
.select(message.id.max())
.from(message)
.where(message.chatRoom.id.eq(chatRoom.id))
.groupBy(message.chatRoom.id)
)).fetchJoin()
.where(isSellerOrWinner(userId))
.fetch();

return sortByLastMessageIdDesc(unsortedDtos);
}

private List<ChatRoomAndMessageAndImageDto> sortByLastMessageIdDesc(
final List<ChatRoomAndMessageAndImageQueryProjectionDto> unsortedDtos
) {
return unsortedDtos.stream()
.sorted(comparing(
(ChatRoomAndMessageAndImageQueryProjectionDto unsortedDto) -> unsortedDto.message().getId()
).reversed()
).map(ChatRoomAndMessageAndImageQueryProjectionDto::toSortedDto)
.toList();
}

private BooleanExpression isSellerOrWinner(final Long userId) {
return (auction.seller.id.eq(userId))
.or(chatRoom.buyer.id.eq(userId));
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
package com.ddang.ddang.chat.infrastructure.persistence;

import com.ddang.ddang.chat.domain.ChatRoom;

import java.util.Optional;

public interface QuerydslChatRoomRepository {

Optional<ChatRoom> findChatRoomById(final Long chatRoomId);

Optional<Long> findChatRoomIdByAuctionId(final Long auctionId);
}
Loading

0 comments on commit 1e0c612

Please sign in to comment.