From 6839802cea3b87c165039924fddc8a91233bc1b4 Mon Sep 17 00:00:00 2001 From: Domae-back-end Date: Tue, 5 Sep 2023 03:53:15 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20Redis=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20Full-text=20?= =?UTF-8?q?=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../idea/model/reponse/IdeaListResponse.java | 1 - .../idea/repository/IdeaRepository.java | 5 ++ .../idea/repository/IdeaRepositoryCustom.java | 11 +++- .../repository/IdeaRepositoryCustomImpl.java | 51 ++++++++++++++++--- .../domain/idea/service/IdeaService.java | 44 ++++++++++++---- .../global/config/QuerydslConfig.java | 19 ------- .../bid/idearush/global/util/RedisUtil.java | 33 ++++++++++-- 7 files changed, 122 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/com/bid/idearush/global/config/QuerydslConfig.java diff --git a/src/main/java/com/bid/idearush/domain/idea/model/reponse/IdeaListResponse.java b/src/main/java/com/bid/idearush/domain/idea/model/reponse/IdeaListResponse.java index 108386f..fbf3a66 100644 --- a/src/main/java/com/bid/idearush/domain/idea/model/reponse/IdeaListResponse.java +++ b/src/main/java/com/bid/idearush/domain/idea/model/reponse/IdeaListResponse.java @@ -15,5 +15,4 @@ public record IdeaListResponse( Long BidWinPrice ) { - } diff --git a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepository.java b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepository.java index ec2b418..26d6389 100644 --- a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepository.java +++ b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepository.java @@ -1,7 +1,10 @@ package com.bid.idearush.domain.idea.repository; import com.bid.idearush.domain.idea.model.entity.Idea; +import com.bid.idearush.domain.idea.type.Category; import jakarta.persistence.LockModeType; +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.Lock; import org.springframework.data.jpa.repository.Modifying; @@ -34,4 +37,6 @@ public interface IdeaRepository extends @Query("UPDATE Idea m SET m.auctionStatus = 'ONGOING' WHERE m.auctionStatus = 'PREPARE' and m.auctionStartTime <= :currentTime") void updatePrepareToOngoing(@Param("currentTime") LocalDateTime currentTime); + Long countByCategory(Category category); + } \ No newline at end of file diff --git a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustom.java b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustom.java index df2dc61..efc4009 100644 --- a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustom.java +++ b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustom.java @@ -1,17 +1,26 @@ package com.bid.idearush.domain.idea.repository; +import com.bid.idearush.domain.idea.model.entity.Idea; import com.bid.idearush.domain.idea.model.reponse.IdeaListResponse; import com.bid.idearush.domain.idea.model.reponse.IdeaOneResponse; import com.bid.idearush.domain.idea.type.Category; +import io.lettuce.core.dynamic.annotation.Param; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import java.util.List; import java.util.Optional; public interface IdeaRepositoryCustom { Page findIdeaAll(Pageable pageable, long count); - Page findCategoryAndTitleAll(Category category, String keyword, Pageable pageable, long count); + + Page findCategory(Pageable pageable, Category category, long count); + + Page findTitle(Pageable pageable, String keyword); + Optional findIdeaOne(Long ideaId); + } diff --git a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustomImpl.java b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustomImpl.java index ab58ba6..6d3e49a 100644 --- a/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustomImpl.java +++ b/src/main/java/com/bid/idearush/domain/idea/repository/IdeaRepositoryCustomImpl.java @@ -5,23 +5,30 @@ import com.bid.idearush.domain.idea.model.entity.QIdea; import com.bid.idearush.domain.idea.model.reponse.IdeaListResponse; import com.bid.idearush.domain.idea.model.reponse.IdeaOneResponse; +import com.bid.idearush.domain.idea.type.AuctionStatus; import com.bid.idearush.domain.idea.type.Category; import com.bid.idearush.domain.user.model.entity.QUsers; import com.bid.idearush.global.type.ServerIpAddress; import com.querydsl.core.types.Expression; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.NumberExpression; +import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.JPQLQuery; import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static org.springframework.util.StringUtils.isEmpty; @@ -31,10 +38,12 @@ public class IdeaRepositoryCustomImpl extends QuerydslRepositorySupport implemen private QUsers qUsers = QUsers.users; private QBid qBid = QBid.bid; private JPAQueryFactory queryFactory; + private EntityManager entityManager; - public IdeaRepositoryCustomImpl(JPAQueryFactory jpaQueryFactory) { + public IdeaRepositoryCustomImpl(EntityManager entityManager) { super(Idea.class); - this.queryFactory = jpaQueryFactory; + this.entityManager = entityManager; + this.queryFactory = new JPAQueryFactory(entityManager); } @Override @@ -85,7 +94,7 @@ public Page findIdeaAll(Pageable pageable, long count) { } @Override - public Page findCategoryAndTitleAll(Category category, String keyword, Pageable pageable, long count) { + public Page findCategory(Pageable pageable, Category category, long count) { List results = queryFactory.select(Projections.constructor(IdeaListResponse.class, qIdea.id, qUsers.nickname.as("writer"), @@ -96,10 +105,7 @@ public Page findCategoryAndTitleAll(Category category, String qIdea.minimumStartingPrice, qIdea.bidWinPrice )) - .where( - ideaTitleContains(keyword), - ideaCategoryEq(category) - ) + .where(ideaCategoryEq(category)) .from(qIdea) .innerJoin(qIdea.users, qUsers) .orderBy(qIdea.createdAt.desc()) @@ -110,10 +116,41 @@ public Page findCategoryAndTitleAll(Category category, String return new PageImpl<>(results, pageable, count); } + @Override + public Page findTitle(Pageable pageable, String keyword) { + + String sql = "SELECT * FROM idea WHERE to_tsvector('english', title) @@ to_tsquery('english', ?1) ORDER BY created_at DESC OFFSET ?2 ROWS FETCH FIRST ?3 ROWS ONLY"; + Query nativeQuery = entityManager.createNativeQuery(sql, Idea.class); + nativeQuery.setParameter(1, keyword+":*"); + nativeQuery.setParameter(2, pageable.getOffset()); + nativeQuery.setParameter(3, pageable.getPageSize()); + List responses = nativeQuery.getResultList(); + + return new PageImpl<>(responses.stream() + .map(idea -> new IdeaListResponse( + idea.getId(), + idea.getUsers().getNickname(), + idea.getTitle(), + idea.getContent(), + idea.getImageName(), + idea.getAuctionStatus(), + idea.getMinimumStartingPrice(), + idea.getBidWinPrice() + )) + .collect(Collectors.toList()), + pageable, 0); + + } + private BooleanExpression ideaTitleContains(String keyword) { return isEmpty(keyword) ? null : qIdea.title.contains(keyword); } + private BooleanExpression test(String keyword) { + return Expressions.booleanTemplate("function('full_text_search', {0}, {1})", + qIdea.title, keyword); + } + private BooleanExpression ideaCategoryEq(Category category) { return isEmpty(category) ? null : qIdea.category.eq(category); } diff --git a/src/main/java/com/bid/idearush/domain/idea/service/IdeaService.java b/src/main/java/com/bid/idearush/domain/idea/service/IdeaService.java index e3c8050..b65bc3b 100644 --- a/src/main/java/com/bid/idearush/domain/idea/service/IdeaService.java +++ b/src/main/java/com/bid/idearush/domain/idea/service/IdeaService.java @@ -19,10 +19,7 @@ import com.bid.idearush.global.util.RedisUtil; import com.bid.idearush.global.util.S3Service; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; +import org.springframework.data.domain.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @@ -30,6 +27,7 @@ import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import static com.bid.idearush.global.type.ServerIpAddress.IMAGE_BASE_PATH; @@ -61,13 +59,27 @@ public Page findAllIdea(String keyword, Category category, Int Sort sort = Sort.by(Sort.Direction.ASC, "createdAt"); Pageable pageable = PageRequest.of(page, 10, sort); - long count = getCount(); + long count = getIdeaCount(); if (!StringUtils.hasText(keyword) && Objects.isNull(category)) { findList = ideaRepository.findIdeaAll(pageable, count); + } else if (!Objects.isNull(category)) { + findList = ideaRepository.findCategory(pageable, category, getCategoryCount(category)); } else { - findList = ideaRepository.findCategoryAndTitleAll(category, keyword, pageable, count); +// findList = ideaRepository.findByKeyword(keyword, pageable) +// .map(idea -> new IdeaListResponse( +// idea.getId(), +// idea.getUsers().getNickname(), +// idea.getTitle(), +// idea.getContent(), +// idea.getImageName(), // 혹시 이미지 URL 조합이 필요하다면 여기에 추가 +// idea.getAuctionStatus(), +// idea.getMinimumStartingPrice(), +// idea.getBidWinPrice() +// )); + findList = ideaRepository.findTitle(pageable,keyword); } + return findList; } @@ -102,7 +114,6 @@ public void createIdea(IdeaRequest ideaRequest, MultipartFile image, Long userId } imageName = image.getOriginalFilename(); } - redisUtil.setIdeaCount(getCount() + 1); Idea newIdea = ideaRequest.toIdea(user, imageName); ideaRepository.save(newIdea); @@ -112,6 +123,8 @@ public void createIdea(IdeaRequest ideaRequest, MultipartFile image, Long userId newIdea.changeImage(uploadPath + "/" + imageName); s3Service.upload(uploadPath, imageName, image); } + redisUtil.addIdeaCount(); + redisUtil.addCategoryCount(ideaRequest.category()); } public void deleteIdea(Long userId, Long ideaId) { @@ -120,9 +133,10 @@ public void deleteIdea(Long userId, Long ideaId) { validateUser(userId, idea); - redisUtil.setIdeaCount(getCount() - 1); s3Service.delete(filePath); ideaRepository.delete(idea); + redisUtil.minIdeaCount(); + redisUtil.minCategoryCount(idea.getCategory()); } private boolean isMultipartFile(MultipartFile multipartFile) { @@ -153,7 +167,7 @@ private void validateUser(Long userId, Idea idea) { } } - private Long getCount() { + private Long getIdeaCount() { Long count = redisUtil.getIdeaCount(); if (Objects.isNull(count)) { count = ideaRepository.count(); @@ -162,4 +176,16 @@ private Long getCount() { return count; } + private Long getCategoryCount(Category category) { + Long count = redisUtil.getCategoryCount(category); + System.out.println("redis : " + count); + if (Objects.isNull(count)) { + count = ideaRepository.countByCategory(category); + System.out.println("category : " + category); + System.out.println("category : " + count); + redisUtil.setCategoryCount(category, count); + } + return count; + } + } \ No newline at end of file diff --git a/src/main/java/com/bid/idearush/global/config/QuerydslConfig.java b/src/main/java/com/bid/idearush/global/config/QuerydslConfig.java deleted file mode 100644 index d244f5d..0000000 --- a/src/main/java/com/bid/idearush/global/config/QuerydslConfig.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.bid.idearush.global.config; - -import com.querydsl.jpa.impl.JPAQueryFactory; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class QuerydslConfig { - @PersistenceContext - private EntityManager entityManager; - - @Bean - public JPAQueryFactory jpaQueryFactory() { - return new JPAQueryFactory(entityManager); - } - -} diff --git a/src/main/java/com/bid/idearush/global/util/RedisUtil.java b/src/main/java/com/bid/idearush/global/util/RedisUtil.java index 572ac25..e014c59 100644 --- a/src/main/java/com/bid/idearush/global/util/RedisUtil.java +++ b/src/main/java/com/bid/idearush/global/util/RedisUtil.java @@ -1,6 +1,7 @@ package com.bid.idearush.global.util; import com.bid.idearush.domain.idea.repository.IdeaRepository; +import com.bid.idearush.domain.idea.type.Category; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @@ -14,15 +15,37 @@ public class RedisUtil { private final RedisTemplate redisTemplate; public Long getIdeaCount() { - Object object = redisTemplate.opsForValue().get("count"); - if(Objects.isNull(object)){ + Object redisIdea = redisTemplate.opsForValue().get("ideaCount"); + if(Objects.isNull(redisIdea)){ return null; } - return Long.parseLong(redisTemplate.opsForValue().get("count").toString()); + return Long.parseLong(redisIdea.toString()); } - public void setIdeaCount(long count) { - redisTemplate.opsForValue().set("count", count); + redisTemplate.opsForValue().set("ideaCount", count); + } + public void addIdeaCount() { + redisTemplate.opsForValue().set("ideaCount", getIdeaCount()+1); + } + public void minIdeaCount() { + redisTemplate.opsForValue().set("ideaCount", getIdeaCount()-1); + } + + public Long getCategoryCount(Category category) { + Object redisCategory = redisTemplate.opsForValue().get(category.toString()); + if(Objects.isNull(redisCategory)){ + return null; + } + return Long.parseLong(redisCategory.toString()); + } + public void setCategoryCount(Category category, Long count) { + redisTemplate.opsForValue().set(category.toString(), count); + } + public void addCategoryCount(Category category) { + redisTemplate.opsForValue().set(category.toString(), getCategoryCount(category)+1); + } + public void minCategoryCount(Category category) { + redisTemplate.opsForValue().set(category.toString(), getCategoryCount(category)-1); } }