Skip to content

Commit

Permalink
Merge pull request #468 from woowacourse-teams/feat/467-inner-join
Browse files Browse the repository at this point in the history
카페 이미지 Fetch Join 시 Inner Join 하도록 변경
  • Loading branch information
nuyh99 authored Sep 26, 2023
2 parents cb81c57 + 6dcdd30 commit f4b6dad
Show file tree
Hide file tree
Showing 20 changed files with 83 additions and 239 deletions.
2 changes: 1 addition & 1 deletion server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ sourceSets {
main.java.srcDirs += [generatedQueryDsl]
}

tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(file(generatedQueryDsl))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.project.yozmcafe.config;

import com.querydsl.jpa.impl.JPAQueryFactory;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.project.yozmcafe.controller.dto.cafe.CafeResponse;
import com.project.yozmcafe.controller.dto.cafe.CafeSearchRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeSearchResponse;
import com.project.yozmcafe.domain.member.Member;
import com.project.yozmcafe.service.CafeService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
Expand All @@ -20,8 +19,7 @@
@RequestMapping("/cafes")
public class CafeController {

private static final int PAGE_SIZE = 5;
private static final int RANKING_PAGE_SIZE = 10;
private static final int CAFE_PAGE_SIZE = 5;

private final CafeService cafeService;

Expand All @@ -31,25 +29,26 @@ public CafeController(final CafeService cafeService) {

@GetMapping
public ResponseEntity<List<CafeResponse>> getCafesForLoggedInMember(@LoginUser final String memberId) {
List<CafeResponse> cafeResponses = cafeService.getCafesForLoginMember(memberId, PAGE_SIZE);
final List<CafeResponse> cafeResponses = cafeService.getCafesForLoginMember(memberId, CAFE_PAGE_SIZE);
return ResponseEntity.ok(cafeResponses);
}

@GetMapping("/guest")
public ResponseEntity<List<CafeResponse>> getCafesForUnLoggedInMember(@PageableDefault(size = PAGE_SIZE) final Pageable pageable) {
public ResponseEntity<List<CafeResponse>> getCafesForUnLoggedInMember(
@PageableDefault(size = CAFE_PAGE_SIZE) final Pageable pageable) {
final List<CafeResponse> cafeResponses = cafeService.getCafesForUnLoginMember(pageable);
return ResponseEntity.ok(cafeResponses);
}

@GetMapping("/ranks")
public ResponseEntity<List<CafeRankResponse>> getCafesOrderByLikeCount(@PageableDefault(size = RANKING_PAGE_SIZE) final Pageable pageable) {
public ResponseEntity<List<CafeRankResponse>> getCafesOrderByLikeCount(@PageableDefault final Pageable pageable) {
final List<CafeRankResponse> cafeRankResponses = cafeService.getCafesOrderByLikeCount(pageable);
return ResponseEntity.ok(cafeRankResponses);
}

@GetMapping("/{cafeId}")
public ResponseEntity<CafeResponse> getCafeById(@PathVariable("cafeId") final long cafeId) {
final CafeResponse cafeResponse = cafeService.getCafeById(cafeId);
final CafeResponse cafeResponse = cafeService.getCafeByIdOrThrow(cafeId);
return ResponseEntity.ok(cafeResponse);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.project.yozmcafe.controller;

import com.project.yozmcafe.controller.dto.cafe.LikedCafeResponse;
import com.project.yozmcafe.controller.dto.cafe.CafeThumbnailResponse;
import com.project.yozmcafe.controller.dto.cafe.LikedCafeResponse;
import com.project.yozmcafe.service.LikedCafeService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.project.yozmcafe.domain.cafe;

import com.project.yozmcafe.exception.BadRequestException;
import jakarta.persistence.*;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import java.util.Objects;

Expand Down Expand Up @@ -71,7 +76,7 @@ private void validate(final String name, final String address, final Images imag
}
}

public Cafe(String name, String address, Images images, Detail detail) {
public Cafe(final String name, final String address, final Images images, final Detail detail) {
this(null, name, address, images, detail, 0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ public interface CafeRepository extends JpaRepository<Cafe, Long>, CafeCustomRep

@Override
@Query("""
select c
from Cafe c
left join fetch c.images.urls
where c.id = :cafeId
SELECT c
FROM Cafe c
JOIN FETCH c.images.urls
WHERE c.id = :cafeId
""")
Optional<Cafe> findById(@Param("cafeId") Long cafeId);

@Override
@Query("""
select c
from Cafe c
left join fetch c.images.urls
SELECT c
FROM Cafe c
JOIN FETCH c.images.urls
""")
List<Cafe> findAll();

@Query("""
SELECT c.id
FROM Cafe c
SELECT c.id
FROM Cafe c
ORDER BY c.likeCount DESC
""")
List<Long> findCafeIdsOrderByLikeCount(Pageable pageable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType;

import java.util.ArrayList;
import java.util.List;

import static com.project.yozmcafe.exception.ErrorCode.DUPLICATED_CAFE_AVAILABLE_TIMES;
Expand All @@ -21,8 +21,8 @@ public class Detail {
public static final int MAP_URL_MAX_LENGTH = 512;
public static final int PHONE_MAX_LENGTH = 20;

@ElementCollection(fetch = FetchType.LAZY)
private List<AvailableTime> availableTimes;
@ElementCollection
private List<AvailableTime> availableTimes = new ArrayList<>();
@Column(nullable = false)
private String mapUrl;
@Column(columnDefinition = "text")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package com.project.yozmcafe.domain.cafe;

import java.util.List;

import com.project.yozmcafe.exception.BadRequestException;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;

import java.util.ArrayList;
import java.util.List;

import static com.project.yozmcafe.exception.ErrorCode.NOT_EXISTED_CAFE_IMAGE;

@Embeddable
public class Images {

private static final int REPRESENTATIVE_INDEX = 0;

@ElementCollection
@CollectionTable(name = "image")
private List<String> urls;
private List<String> urls = new ArrayList<>();

protected Images() {
}

public Images(List<String> urls) {
public Images(final List<String> urls) {
if (urls.isEmpty()) {
throw new BadRequestException(NOT_EXISTED_CAFE_IMAGE);
}

this.urls = urls;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ public List<UnViewedCafe> getNextUnViewedCafes(final int size) {
}

public List<Cafe> getLikedCafes(final int startIndex, final int endIndex) {
final List<LikedCafe> likedCafes = new ArrayList<>(this.likedCafes);
final List<Cafe> cafes = likedCafes.stream()
.map(LikedCafe::getCafe)
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,17 @@
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

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

@Repository
public interface MemberRepository extends JpaRepository<Member, String> {

@Override
@Query("""
select m
from Member m
left join fetch m.unViewedCafes uvc
left join fetch uvc.cafe
where m.id = :id
SELECT m
FROM Member m
JOIN FETCH m.unViewedCafes uvc
JOIN FETCH uvc.cafe
WHERE m.id = :id
""")
Optional<Member> findById(@Param("id") String id);

@Override
@Query("""
select m
from Member m
left join fetch m.unViewedCafes uvc
left join fetch uvc.cafe
""")
List<Member> findAll();
Optional<Member> findWithUnViewedCafesById(@Param("id") String id);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package com.project.yozmcafe.exception;

import static java.lang.String.format;

import com.project.yozmcafe.domain.cafe.Cafe;
import com.project.yozmcafe.domain.cafe.Detail;

import static com.project.yozmcafe.domain.CafeRankGenerator.MAX_RANK;

import com.project.yozmcafe.domain.resizedimage.ImageResizer;

import static java.lang.String.format;

public enum ErrorCode {
TOKEN_NOT_EXIST("T1", "토큰이 존재하지 않습니다."),
TOKEN_IS_EXPIRED("T2", "만료된 토큰입니다."),
Expand All @@ -35,8 +32,6 @@ public enum ErrorCode {
NOT_EXISTED_OAUTH_PROVIDER("O2", "잘못된 Provider Name 입니다."),
NOT_EXISTED_OAUTH_CLIENT("O3", "일치하는 OAuthClient가 존재하지 않습니다."),

RANK_OUT_OF_BOUNDS("R1", format("좋아요 개수 랭킹 범위를 벗어나는 요청입니다. 좋아요 랭킹은 %d위까지 가능합니다. 요청의 페이지 확인이 필요합니다.", MAX_RANK)),

NOT_IMAGE("I1", "이미지 형식만 받을 수 있습니다."),
INVALID_IMAGE_SIZE("I2", format("이미지는 최대 %d Byte까지만 가능합니다.", ImageResizer.MAX_IMAGE_SIZE)),

Expand Down
38 changes: 23 additions & 15 deletions server/src/main/java/com/project/yozmcafe/service/CafeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@
import com.project.yozmcafe.controller.dto.cafe.CafeResponse;
import com.project.yozmcafe.controller.dto.cafe.CafeSearchRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeSearchResponse;
import com.project.yozmcafe.domain.CafeRankGenerator;
import com.project.yozmcafe.domain.cafe.Cafe;
import com.project.yozmcafe.domain.cafe.CafeRepository;
import com.project.yozmcafe.domain.cafe.UnViewedCafe;
import com.project.yozmcafe.domain.member.Member;
import com.project.yozmcafe.domain.member.MemberRepository;
import com.project.yozmcafe.exception.BadRequestException;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

import static com.project.yozmcafe.exception.ErrorCode.NOT_EXISTED_CAFE;
import static com.project.yozmcafe.exception.ErrorCode.NOT_EXISTED_MEMBER;
import static io.micrometer.common.util.StringUtils.isBlank;

@Service
@Transactional(readOnly = true)
public class CafeService {

private final CafeRepository cafeRepository;
private final MemberService memberService;
private final MemberRepository memberRepository;
private final UnViewedCafeService unViewedCafeService;
private final CafeRankGenerator cafeRankGenerator;

public CafeService(final CafeRepository cafeRepository, final MemberService memberService,
final UnViewedCafeService unViewedCafeService, final CafeRankGenerator cafeRankGenerator) {
public CafeService(final CafeRepository cafeRepository, final MemberRepository memberRepository,
final UnViewedCafeService unViewedCafeService) {
this.cafeRepository = cafeRepository;
this.memberService = memberService;
this.memberRepository = memberRepository;
this.unViewedCafeService = unViewedCafeService;
this.cafeRankGenerator = cafeRankGenerator;
}

public List<CafeResponse> getCafesForUnLoginMember(final Pageable pageable) {
Expand All @@ -45,19 +45,22 @@ public List<CafeResponse> getCafesForUnLoginMember(final Pageable pageable) {
}

public List<CafeRankResponse> getCafesOrderByLikeCount(final Pageable pageable) {
cafeRankGenerator.validatePage(pageable);

final List<Long> ids = cafeRepository.findCafeIdsOrderByLikeCount(pageable);
final List<Cafe> foundCafes = cafeRepository.findCafesByIdsOrderByLikeCount(ids);
final List<Cafe> cafes = cafeRepository.findCafesByIdsOrderByLikeCount(ids);

return foundCafes.stream()
.map(cafe -> CafeRankResponse.of(cafeRankGenerator.makeRank(foundCafes.indexOf(cafe), pageable), cafe))
.toList();
final List<CafeRankResponse> response = new ArrayList<>();

int rank = (int) pageable.getOffset();
for (final Cafe cafe : cafes) {
response.add(CafeRankResponse.of(++rank, cafe));
}

return response;
}

@Transactional
public List<CafeResponse> getCafesForLoginMember(final String memberId, final int size) {
final Member member = memberService.findMemberByIdOrElseThrow(memberId);
final Member member = getMemberByIdOrThrow(memberId);
final List<UnViewedCafe> cafes = member.getNextUnViewedCafes(size);
unViewedCafeService.refillWhenUnViewedCafesSizeUnderTwenty(member);

Expand All @@ -67,7 +70,12 @@ public List<CafeResponse> getCafesForLoginMember(final String memberId, final in
.toList();
}

public CafeResponse getCafeById(final long cafeId) {
private Member getMemberByIdOrThrow(final String memberId) {
return memberRepository.findWithUnViewedCafesById(memberId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTED_MEMBER));
}

public CafeResponse getCafeByIdOrThrow(final long cafeId) {
final Cafe foundCafe = cafeRepository.findById(cafeId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTED_CAFE));

Expand Down
1 change: 1 addition & 0 deletions server/src/main/resources/application-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ spring.flyway.enabled=true
spring.flyway.baseline-version=20230901153300
spring.flyway.baseline-on-migrate=true
spring.flyway.out-of-order=true
spring.jpa.properties.hibernate.default_batch_fetch_size=1000
Original file line number Diff line number Diff line change
Expand Up @@ -324,19 +324,6 @@ void getCafesOrderByLikeCountWhenNotExist() {
);
}

@Test
@DisplayName("/cafes/rank?page=? 요청을 보낼 때, 순위 범위를 초과하는 요청이면 statusCode 400을 응답한다")
void getCafesOrderByLikeCountWhenRankOutBoundFail() {
//when
final Response response = given(spec).log().all()
.filter(document(CAFE_API + "좋아요 개수 순위에 따라 카페정보 조회 - 범위 초과 예외", getPageRequestParam()))
.when()
.get("/cafes/ranks?page=4");

//then
assertThat(response.statusCode()).isEqualTo(400);
}

@Test
@DisplayName("사용자가 검색 요청을 보내면, 검색어와 기준에 맞는 카페를 응답한다.")
void getCafesBySearch() {
Expand Down
Loading

0 comments on commit f4b6dad

Please sign in to comment.