Skip to content
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
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ repositories {
}

dependencies {
// 스프링 배치
implementation 'org.springframework.boot:spring-boot-starter-batch'

implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
Expand All @@ -36,7 +39,7 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.security:spring-security-test®'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.sookmyung.concon.Review.configuration;

import com.sookmyung.concon.Review.configuration.chunk.ReviewItemProcessor;
import com.sookmyung.concon.Review.configuration.chunk.ReviewItemReader;
import com.sookmyung.concon.Review.configuration.chunk.ReviewItemWriter;
import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import com.sookmyung.concon.Review.entity.Review;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@EnableBatchProcessing
@Slf4j
@RequiredArgsConstructor
public class ReviewBatchConfig {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;

@Bean
public Job reviewCreateJob(Step reviewCreateStep) {
return new JobBuilder("reviewCreateJob", jobRepository)
.start(reviewCreateStep)
.build();
}

@Bean
public Step reviewCreateStep(ReviewItemReader reader, ReviewItemProcessor processor,
ReviewItemWriter writer) {
return new StepBuilder("reviewCreateStep", jobRepository)
.<ReviewRedisDto, Review>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.sookmyung.concon.Review.configuration.chunk;

import com.sookmyung.concon.Coupon.Entity.Coupon;
import com.sookmyung.concon.Coupon.service.CouponFacade;
import com.sookmyung.concon.Review.dto.ReviewCreateRequestDto;
import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import com.sookmyung.concon.Review.entity.Review;
import com.sookmyung.concon.User.Entity.User;
import com.sookmyung.concon.User.service.UserFacade;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class ReviewItemProcessor implements ItemProcessor<ReviewRedisDto, Review> {
private final UserFacade userFacade;
private final CouponFacade couponFacade;

@Override
public Review process(ReviewRedisDto dto) throws Exception {

User user = userFacade.findUserByToken(dto.getToken());
ReviewCreateRequestDto request = dto.getRequest();
Coupon coupon = couponFacade.findByCouponId(request.getCouponId());
return request.toEntity(user, coupon);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.sookmyung.concon.Review.configuration.chunk;

import com.sookmyung.concon.Review.dto.ReviewCreateRequestDto;
import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.NonTransientResourceException;
import org.springframework.batch.item.ParseException;
import org.springframework.batch.item.UnexpectedInputException;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class ReviewItemReader implements ItemReader<ReviewRedisDto> {
private List<ReviewRedisDto> reviewRedisDtoList;
private int nextIndex;

public void setReviewRedisDtoList(List<ReviewRedisDto> reviewRedisDtoList) {
this.reviewRedisDtoList = reviewRedisDtoList;
this.nextIndex = 0;
}

@Override
public ReviewRedisDto read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if (nextIndex < reviewRedisDtoList.size()) {
return reviewRedisDtoList.get(nextIndex++);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.sookmyung.concon.Review.configuration.chunk;

import com.sookmyung.concon.Review.entity.Review;
import com.sookmyung.concon.Review.repository.ReviewRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ItemWriter;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class ReviewItemWriter implements ItemWriter<Review> {
private final ReviewRepository reviewRepository;
@Override
public void write(Chunk<? extends Review> reviews) throws Exception {
reviewRepository.saveAll(reviews);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.sookmyung.concon.Review.controller;

import com.sookmyung.concon.Review.configuration.chunk.ReviewItemReader;
import com.sookmyung.concon.Review.dto.ReviewCreateRequestDto;
import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import com.sookmyung.concon.Review.dto.ReviewUpdateRequestDto;
import com.sookmyung.concon.Review.entity.Review;
import com.sookmyung.concon.Review.repository.ReviewRedisRepository;
import com.sookmyung.concon.Review.service.ReviewBatchService;
import com.sookmyung.concon.Review.service.ReviewService;
import com.sookmyung.concon.User.Jwt.JwtUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
Expand All @@ -21,7 +27,26 @@
@RequestMapping("/api/reviews")
@RequiredArgsConstructor
public class ReviewController {
private final JobLauncher jobLauncher;
private final Job reviewCreateJob;
private final ReviewItemReader itemReader;
private final ReviewRedisRepository reviewRedisRepository;
private final ReviewService reviewService;
private final ReviewBatchService reviewBatchService;

@Operation(summary = "리뷰 10개 생성 시 한꺼번에 저장 batch")
@PostMapping("/reviews/batch")
public ResponseEntity<?> createReviewBatch(
@RequestBody ReviewCreateRequestDto request,
@RequestHeader("Authorization") String token) {

try {
reviewBatchService.createReview(request, token);
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}

@Operation(summary = "후기 작성")
@PostMapping
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/sookmyung/concon/Review/dto/ReviewRedisDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.sookmyung.concon.Review.dto;

import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
public class ReviewRedisDto {
private ReviewCreateRequestDto request;
private String token;

public static ReviewRedisDto toDto(ReviewCreateRequestDto request, String token) {
return ReviewRedisDto.builder()
.request(request)
.token(token)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.sookmyung.concon.Review.repository;

import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.UUID;

@Repository
public class ReviewRedisRepository {
private RedisTemplate<String, Object> redisTemplate;
private static final String REVIEW_KEY = "reviews";

public void save(ReviewRedisDto dto) {
String reviewId = UUID.randomUUID().toString();
redisTemplate.opsForHash().put(REVIEW_KEY, reviewId, dto);
}

public int size() {
return redisTemplate.opsForHash().size(REVIEW_KEY).intValue();
}

public List<ReviewRedisDto> getAndClearReviewsDtoList() {
List<ReviewRedisDto> reviews = redisTemplate.opsForHash().values(REVIEW_KEY)
.stream()
.map(obj -> (ReviewRedisDto) obj)
.toList();

redisTemplate.delete(REVIEW_KEY);

return reviews;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.sookmyung.concon.Review.service;

import com.sookmyung.concon.Review.configuration.chunk.ReviewItemReader;
import com.sookmyung.concon.Review.dto.ReviewCreateRequestDto;
import com.sookmyung.concon.Review.dto.ReviewRedisDto;
import com.sookmyung.concon.Review.repository.ReviewRedisRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class ReviewBatchService {
private final ReviewRedisRepository reviewRedisRepository;
private final ReviewItemReader reviewItemReader;
private final JobLauncher jobLauncher;
private final Job reviewCreateJob;

public void createReview(ReviewCreateRequestDto request, String token) throws Exception{
ReviewRedisDto dto = ReviewRedisDto.toDto(request, token);
reviewRedisRepository.save(dto);

if (reviewRedisRepository.size() >= 10) {
runBatchJob();
}
}
private void runBatchJob() throws Exception {
List<ReviewRedisDto> reviewRedisDtoList = reviewRedisRepository.getAndClearReviewsDtoList();
reviewItemReader.setReviewRedisDtoList(reviewRedisDtoList);

JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();

jobLauncher.run(reviewCreateJob, jobParameters);
}
}
Loading