Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] 마이페이지 조회에 fetch join 적용했어요 #177

Merged
merged 4 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
import org.springframework.data.repository.query.Param;
import team7.inplace.likedPlace.domain.LikedPlace;

import java.util.Optional;

public interface LikedPlaceRepository extends JpaRepository<LikedPlace, Long> {

Optional<LikedPlace> findByUserIdAndPlaceId(Long userId, Long placeId);

Page<LikedPlace> findByUserIdAndIsLikedTrue(Long userId, Pageable pageable);

@Query("SELECT l.place.id FROM LikedPlace l WHERE l.user.id = :userId AND l.isLiked = true")
Set<Long> findPlaceIdsByUserIdAndIsLikedTrue(@Param("userId") Long userId);

@Query("SELECT lp FROM LikedPlace lp JOIN FETCH lp.place WHERE lp.user.id = :userId AND lp.isLiked = true")
Page<LikedPlace> findByUserIdAndIsLikedTrueWithPlace(@Param("userId") Long userId,
Pageable pageable);

}
157 changes: 80 additions & 77 deletions src/main/java/team7/inplace/place/application/PlaceService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package team7.inplace.place.application;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
Expand Down Expand Up @@ -28,9 +34,6 @@
import team7.inplace.video.domain.Video;
import team7.inplace.video.persistence.VideoRepository;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class PlaceService {
Expand All @@ -44,29 +47,29 @@ public class PlaceService {
private final LikedPlaceRepository likedPlaceRepository;

public Page<PlaceInfo> getPlacesWithinRadius(
PlacesCoordinateCommand placesCoordinateCommand,
PlacesFilterParamsCommand placesFilterParamsCommand) {
PlacesCoordinateCommand placesCoordinateCommand,
PlacesFilterParamsCommand placesFilterParamsCommand) {

// categories와 influencers 필터 처리
List<String> categoryFilters = placesFilterParamsCommand.isCategoryFilterExists()
? Arrays.stream(placesFilterParamsCommand.categories().split(",")).toList()
: null;
? Arrays.stream(placesFilterParamsCommand.categories().split(",")).toList()
: null;

List<String> influencerFilters = placesFilterParamsCommand.isInfluencerFilterExists()
? Arrays.stream(placesFilterParamsCommand.influencers().split(",")).toList()
: null;
? Arrays.stream(placesFilterParamsCommand.influencers().split(",")).toList()
: null;

// 주어진 좌표로 장소를 찾고, 해당 페이지의 결과를 가져옵니다.
Page<Place> placesPage = getPlacesByDistance(placesCoordinateCommand, categoryFilters,
influencerFilters);
influencerFilters);

// Place ID 목록 추출
List<Long> placeIds = getPlaceIds(placesPage);

// influencer 조회 => video->Map(placeId, influencerName)
List<Video> videos = videoRepository.findByPlaceIdIn(placeIds);
Map<Long, String> placeIdToInfluencerName = getMapPlaceIdToInfluencerName(
videos);
videos);

// PlaceInfo 생성
List<PlaceInfo> placeInfos = convertToPlaceInfos(placesPage, placeIdToInfluencerName);
Expand All @@ -76,52 +79,52 @@ public Page<PlaceInfo> getPlacesWithinRadius(
}

private List<PlaceInfo> convertToPlaceInfos(Page<Place> placesPage,
Map<Long, String> placeIdToInfluencerName) {
Map<Long, String> placeIdToInfluencerName) {
return placesPage.getContent().stream()
.map(place -> {
// map에서 조회되지 않은 placeId는 null로 처리
String influencerName = placeIdToInfluencerName.getOrDefault(place.getId(), null);
return PlaceInfo.of(place, influencerName, isLikedPlace(place.getId()));
})
.toList();
.map(place -> {
// map에서 조회되지 않은 placeId는 null로 처리
String influencerName = placeIdToInfluencerName.getOrDefault(place.getId(), null);
return PlaceInfo.of(place, influencerName, isLikedPlace(place.getId()));
})
.toList();
}

private Map<Long, String> getMapPlaceIdToInfluencerName(List<Video> videos) {
return videos.stream()
.collect(Collectors.toMap(
video -> video.getPlace().getId(),
video -> video.getInfluencer().getName(),
(existing, replacement) -> existing
));
.collect(Collectors.toMap(
video -> video.getPlace().getId(),
video -> video.getInfluencer().getName(),
(existing, replacement) -> existing
));
}

private List<Long> getPlaceIds(Page<Place> placesPage) {
return placesPage.getContent().stream()
.map(Place::getId)
.toList();
.map(Place::getId)
.toList();
}

private Page<Place> getPlacesByDistance(
PlacesCoordinateCommand placesCoordinateCommand,
List<String> categoryFilters,
List<String> influencerFilters
PlacesCoordinateCommand placesCoordinateCommand,
List<String> categoryFilters,
List<String> influencerFilters
) {
return placeRepository.findPlacesByDistanceAndFilters(
placesCoordinateCommand.topLeftLongitude(),
placesCoordinateCommand.topLeftLatitude(),
placesCoordinateCommand.bottomRightLongitude(),
placesCoordinateCommand.bottomRightLatitude(),
placesCoordinateCommand.longitude(),
placesCoordinateCommand.latitude(),
categoryFilters,
influencerFilters,
placesCoordinateCommand.pageable()
placesCoordinateCommand.topLeftLongitude(),
placesCoordinateCommand.topLeftLatitude(),
placesCoordinateCommand.bottomRightLongitude(),
placesCoordinateCommand.bottomRightLatitude(),
placesCoordinateCommand.longitude(),
placesCoordinateCommand.latitude(),
categoryFilters,
influencerFilters,
placesCoordinateCommand.pageable()
);
}

public PlaceDetailInfo getPlaceDetailInfo(Long placeId) {
Place place = placeRepository.findById(placeId)
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));

Video video = null;
List<Video> videos = videoRepository.findByPlaceId(placeId);
Expand All @@ -135,26 +138,26 @@ public PlaceDetailInfo getPlaceDetailInfo(Long placeId) {

public List<Long> createPlaces(List<Create> placeCommands) {
var places = placeCommands.stream()
.map(command -> {
if (Objects.isNull(command)) {
return null;
}
return command.toEntity();
})
.toList();
.map(command -> {
if (Objects.isNull(command)) {
return null;
}
return command.toEntity();
})
.toList();
var nonNullPlaces = places.stream()
.filter(Objects::nonNull)
.toList();
.filter(Objects::nonNull)
.toList();
placeRepository.saveAll(nonNullPlaces);

var savedPlacesId = places.stream()
.map(place -> {
if (Objects.isNull(place)) {
return -1L;
}
return place.getId();
})
.toList();
.map(place -> {
if (Objects.isNull(place)) {
return -1L;
}
return place.getId();
})
.toList();

return savedPlacesId;
}
Expand All @@ -166,18 +169,18 @@ public void likeToPlace(PlaceLikeCommand comm) {
private void findOrCreateLikedPlace(Long placeId, boolean likes) {

Long userId = getUserId().orElseThrow(
() -> InplaceException.of(AuthorizationErrorCode.TOKEN_IS_EMPTY)
() -> InplaceException.of(AuthorizationErrorCode.TOKEN_IS_EMPTY)
);

LikedPlace likedPlace = likedPlaceRepository.findByUserIdAndPlaceId(userId, placeId)
.orElseGet(() -> {
// 존재하지 않는 경우에만 Place 조회 후 likedPlace 생성
Place place = placeRepository.findById(placeId)
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));
User user = userRepository.findById(userId)
.orElseThrow(() -> InplaceException.of(UserErrorCode.NOT_FOUND));
return new LikedPlace(user, place);
});
.orElseGet(() -> {
// 존재하지 않는 경우에만 Place 조회 후 likedPlace 생성
Place place = placeRepository.findById(placeId)
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));
User user = userRepository.findById(userId)
.orElseThrow(() -> InplaceException.of(UserErrorCode.NOT_FOUND));
return new LikedPlace(user, place);
});

likedPlace.updateLike(likes);
likedPlaceRepository.save(likedPlace);
Expand All @@ -191,9 +194,9 @@ private Optional<Long> getUserId() {

private boolean isLikedPlace(Long placeId) {
return getUserId()
.flatMap(userId -> likedPlaceRepository.findByUserIdAndPlaceId(userId, placeId))
.map(LikedPlace::isLiked)
.orElse(false);
.flatMap(userId -> likedPlaceRepository.findByUserIdAndPlaceId(userId, placeId))
.map(LikedPlace::isLiked)
.orElse(false);
}

public Long createPlace(Create placeCommand) {
Expand All @@ -204,30 +207,30 @@ public Long createPlace(Create placeCommand) {

public PlaceMessageCommand getPlaceMessageCommand(Long placeId) {
Place place = placeRepository.findById(placeId)
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));
.orElseThrow(() -> InplaceException.of(PlaceErrorCode.NOT_FOUND));

Video video = videoRepository.findByPlaceId(placeId)
.stream().findFirst().orElse(null);
.stream().findFirst().orElse(null);

Influencer influencer = (video != null) ? video.getInfluencer() : null;

return PlaceMessageCommand.of(place, influencer, video);
}

public Page<LikedPlaceInfo> getLikedPlaceInfo(Long userId, Pageable pageable) {
Page<LikedPlace> placePage = likedPlaceRepository.findByUserIdAndIsLikedTrue(userId,
pageable);
Page<LikedPlace> placePage = likedPlaceRepository.findByUserIdAndIsLikedTrueWithPlace(
userId, pageable);
List<Long> placeIds = placePage.map(likedPlace -> likedPlace.getPlace().getId()).toList();
List<Video> videos = videoRepository.findByPlaceIdIn(placeIds);
List<Video> videos = videoRepository.findByPlaceIdInWithInfluencer(placeIds);
Map<Long, String> placeIdToInfluencerName = getMapPlaceIdToInfluencerName(videos);

List<LikedPlaceInfo> likedPlaceInfos = placePage.getContent().stream()
.map(likedPlace -> {
String influencerName = placeIdToInfluencerName.getOrDefault(
likedPlace.getPlace().getId(), null);
return LikedPlaceInfo.of(likedPlace, influencerName);
})
.toList();
.map(likedPlace -> {
String influencerName = placeIdToInfluencerName.getOrDefault(
likedPlace.getPlace().getId(), null);
return LikedPlaceInfo.of(likedPlace, influencerName);
})
.toList();

return new PageImpl<>(likedPlaceInfos, pageable, placePage.getTotalElements());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public void deleteReview(Long reviewId) {

@Transactional(readOnly = true)
public Page<MyReviewInfo> getMyReviews(Long userId, Pageable pageable) {
Page<Review> reviewPage = reviewRepository.findByUserId(userId, pageable);
Page<Review> reviewPage = reviewRepository.findByUserIdWithPlace(userId, pageable);

return reviewPage.map(MyReviewInfo::from);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import team7.inplace.review.domain.Review;

public interface ReviewRepository extends JpaRepository<Review, Long> {
Expand All @@ -11,5 +12,6 @@ public interface ReviewRepository extends JpaRepository<Review, Long> {

Page<Review> findByPlaceId(Long placeId, Pageable pageable);

Page<Review> findByUserId(Long userId, Pageable pageable);
@Query("SELECT r FROM Review r JOIN FETCH r.place WHERE r.user.id = :userId")
Page<Review> findByUserIdWithPlace(Long userId, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package team7.inplace.video.persistence;

import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import team7.inplace.place.domain.Place;
import team7.inplace.video.domain.Video;

import java.util.List;
import java.util.Optional;

public interface VideoRepository extends JpaRepository<Video, Long> {

@Query("SELECT v FROM Video v JOIN FETCH v.place JOIN FETCH v.influencer ORDER BY v.viewCountIncrease DESC")
List<Video> findTop10ByOrderByViewCountIncreaseDesc(Pageable pageable);

Expand All @@ -23,12 +23,15 @@ public interface VideoRepository extends JpaRepository<Video, Long> {
Optional<Video> findTopByPlaceOrderByIdDesc(Place place);

@Query(
value = "SELECT v FROM Video v JOIN FETCH v.influencer WHERE v.place IS NULL",
countQuery = "SELECT COUNT(v) FROM Video v"
value = "SELECT v FROM Video v JOIN FETCH v.influencer WHERE v.place IS NULL",
countQuery = "SELECT COUNT(v) FROM Video v"
)
Page<Video> findAllByPlaceIsNull(Pageable pageable);

List<Video> findByPlaceIdIn(List<Long> placeIds);

List<Video> findByPlaceId(Long placeId);

@Query("SELECT v FROM Video v JOIN FETCH v.influencer WHERE v.place.id IN :placeIds")
List<Video> findByPlaceIdInWithInfluencer(List<Long> placeIds);
}