Skip to content
This repository has been archived by the owner on Aug 13, 2022. It is now read-only.

Commit

Permalink
#29 게시글 캐싱시 리스트가 아닌 각각 캐싱하게 변경
Browse files Browse the repository at this point in the history
- redisTemplate과 Product관련 데이터를 reids에 CRUD하는 것들을 ProdcutDao로 변경
- Product 검색시 필요한 상수 데이터들 ProductDTO로 변경 (검색 갯수, 캐시명, 카테고리 정보)
  • Loading branch information
junshock5 committed Sep 8, 2020
1 parent 9e6b73e commit 6387b2d
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class ProductSearchController {
*/
@GetMapping
public ProductSearchResponse search(ProductDTO productDTO,CategoryDTO categoryDTO) {
String accountId = productSearchService.DEFAULT_PRODUCT_CACHE_KEY;
String accountId = ProductDTO.DEFAULT_PRODUCT_SEARCH_CACHE_KEY;
List<ProductDTO> productDTOList = productSearchService.findAllProductsByCacheId(accountId);

if(productDTOList.size() == 0)
Expand Down
93 changes: 93 additions & 0 deletions src/main/java/com/market/server/dao/ProductDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.market.server.dao;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.server.dto.CategoryDTO;
import com.market.server.dto.ProductDTO;
import com.market.server.mapper.ProductSearchMapper;
import com.market.server.utils.RedisKeyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Repository
public class ProductDao {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private ProductSearchMapper productSearchMapper;

@Autowired
private ObjectMapper objectMapper;

@Value("${expire.products}")
private long productsExpireSecond;

// 상위 2000개 게시글을 redisTemplate에 push
@PostConstruct
public void init() {
System.out.println("ProductSearchServiceImpl @PostConstruct init redisTemplate에 push");

CategoryDTO categoryDTO = CategoryDTO.builder()
.id(ProductDTO.DEFAULT_SEARCH_CATEGORY_ID)
.name(ProductDTO.DEFAULT_SEARCH_CATEGORY_NAME)
.sortStatus(CategoryDTO.SortStatus.NEWEST)
.searchCount(ProductDTO.DEFAULT_PRODUCT_CACHE_COUNT)
.pagingStartOffset(CategoryDTO.PAGING_OFFSET)
.build();

List<ProductDTO> productDTOList = productSearchMapper.selectProducts(ProductDTO.Status.NEW.toString(),categoryDTO);

for (ProductDTO productDTO : productDTOList) {

final String key = RedisKeyFactory.generateProductKey(productDTO.DEFAULT_PRODUCT_SEARCH_CACHE_KEY);

redisTemplate.watch(key); // 해당 키를 감시한다. 변경되면 로직 취소.

try {
if (redisTemplate.opsForList().size(key) > 2000) {
throw new IndexOutOfBoundsException("최상단 중고물품 2O00개 이상 담을 수 없습니다.");
}

redisTemplate.multi();
redisTemplate.opsForList().rightPush(key, productDTO);
redisTemplate.expire(key, productsExpireSecond, TimeUnit.SECONDS);

redisTemplate.exec();
} catch (Exception e) {
redisTemplate.discard(); // 트랜잭션 종료시 unwatch()가 호출된다
throw e;
}
}
}

// 스프링 컨테이너에서 객체인 빈을 제거하기 전에 redisTemplate에 push된 게시물들 삭제
@PreDestroy
public void destory(){
redisTemplate.delete(RedisKeyFactory.generateProductKey(ProductDTO.DEFAULT_PRODUCT_SEARCH_CACHE_KEY));
}

/**
* 최상단 중고물품들을 캐싱된 레디스에서 조회한다.
* @author topojs8
* @param useId 고객 아이디
* @return
*/
public List<ProductDTO> findAllProductsByCacheId(String useId) {
List<ProductDTO> items = redisTemplate.opsForList()
.range(RedisKeyFactory.generateProductKey(useId), 0, -1)
.stream()
.map(item -> objectMapper.convertValue(item, ProductDTO.class))
.collect(Collectors.toList());
return items;
}

}
6 changes: 5 additions & 1 deletion src/main/java/com/market/server/dto/ProductDTO.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package com.market.server.dto;

import lombok.*;
import java.time.LocalDateTime;
import java.util.Date;

@Getter
@Setter
@ToString
@NoArgsConstructor
public class ProductDTO {
public static final int DEFAULT_SEARCH_CATEGORY_ID = 1;
public static final int DEFAULT_PRODUCT_CACHE_COUNT = 2000;
public static final String DEFAULT_SEARCH_CATEGORY_NAME = CategoryDTO.SortStatus.CATEGORIES.toString();
public static final String DEFAULT_PRODUCT_SEARCH_CACHE_KEY = "noLogin";

public enum Status {
NEW, OLD, ECT
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,117 +1,29 @@
package com.market.server.service.Impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.server.dao.ProductDao;
import com.market.server.dto.CategoryDTO;
import com.market.server.dto.ProductDTO;
import com.market.server.mapper.ProductSearchMapper;
import com.market.server.service.ProductSearchService;
import com.market.server.utils.RedisKeyFactory;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Log4j2
public class ProductSearchServiceImpl implements ProductSearchService {

private final ProductSearchMapper productSearchMapper;
private static final int DEFAULT_SEARCH_CATEGORY_ID = 1;
private static final int DEFAULT_PRODUCT_CACHE_COUNT = 2000;
private static final String DEFAULT_SEARCH_CATEGORY_NAME = CategoryDTO.SortStatus.CATEGORIES.toString();
public static final String DEFAULT_PRODUCT_CACHE_KEY = "noLogin";

@Autowired
private RedisTemplate<String, Object> redisTemplate;
private final ProductDao productDao;

@Autowired
private ObjectMapper objectMapper;

@Value("${expire.products}")
private long productsExpireSecond;

public ProductSearchServiceImpl(ProductSearchMapper productSearchMapper)
public ProductSearchServiceImpl(ProductSearchMapper productSearchMapper, ProductDao productDao)
{
this.productSearchMapper = productSearchMapper;

}

// 상위 2000개 게시글을 redisTemplate에 push
@PostConstruct
public void init() {
System.out.println("ProductSearchServiceImpl @PostConstruct init redisTemplate에 push");

CategoryDTO categoryDTO = CategoryDTO.builder()
.id(DEFAULT_SEARCH_CATEGORY_ID)
.name(DEFAULT_SEARCH_CATEGORY_NAME)
.sortStatus(CategoryDTO.SortStatus.NEWEST)
.searchCount(DEFAULT_PRODUCT_CACHE_COUNT)
.pagingStartOffset(CategoryDTO.PAGING_OFFSET)
.build();

List<ProductDTO> productDTOList = productSearchMapper.selectProducts(ProductDTO.Status.NEW.toString(),categoryDTO);

for (ProductDTO productDTO : productDTOList) {

final String key = RedisKeyFactory.generateProductKey(DEFAULT_PRODUCT_CACHE_KEY);

redisTemplate.watch(key); // 해당 키를 감시한다. 변경되면 로직 취소.

try {
if (redisTemplate.opsForList().size(key) > 2000) {
throw new IndexOutOfBoundsException("최상단 중고물품 2O00개 이상 담을 수 없습니다.");
}

redisTemplate.multi();
redisTemplate.opsForList().rightPush(key, productDTO);
redisTemplate.expire(key, productsExpireSecond, TimeUnit.SECONDS);

redisTemplate.exec();
} catch (Exception e) {
redisTemplate.discard(); // 트랜잭션 종료시 unwatch()가 호출된다
throw e;
}
}
}

public void addRedisKeys(ProductDTO productDTO, String userId) {
final String key = RedisKeyFactory.generateProductKey(userId);

redisTemplate.watch(key); // 해당 키를 감시한다. 변경되면 로직 취소.

try {
if (redisTemplate.opsForList().size(key) >= 2000) {
throw new IndexOutOfBoundsException("최상단 중고물품 2O00개 이상 담을 수 없습니다.");
}

redisTemplate.multi();
redisTemplate.opsForList().rightPush(key, productDTO);
redisTemplate.expire(key, productsExpireSecond, TimeUnit.SECONDS);
redisTemplate.exec();

} catch (Exception e) {
redisTemplate.discard(); // 트랜잭션 종료시 unwatch()가 호출된다
throw e;
}
this.productDao = productDao;
}

/**
* 최상단 중고물품들을 조회한다.
* @author topojs8
* @param useId 고객 아이디
* @return
*/
public List<ProductDTO> findAllProductsByCacheId(String useId) {
List<ProductDTO> items = redisTemplate.opsForList()
.range(RedisKeyFactory.generateProductKey(useId), 0, -1)
.stream()
.map(item -> objectMapper.convertValue(item, ProductDTO.class))
.collect(Collectors.toList());
return items;
return productDao.findAllProductsByCacheId(useId);
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ expire.defaultTime=36288000
expire.products=3600

# tomcat
server.port=8888
server.port=9000

0 comments on commit 6387b2d

Please sign in to comment.