todo
처음에는 batch를 쓸 필요성을 못느끼고 @Scheduled 어노테이션을 사용해서 자정마다 살행되도록 할 생각이었다. 1번 케이스의 경우에는 대용량 데이터 처리 없이 단순히 dump 명령어만을 사용하여 파일을 저장하기만 하면 되기 때문에 batch를 쓸 필요는 없었다. 하지만 2번의 경우에는 상품이 많아지면 update해야 할 row들이 늘어나기 때문에 데이터 처리 과정을 관리할 필요가 있었기 때문에 @Scheduled 와 Spring Batch 를 같이 사용하기로 하였다. 또한 Spring Batch를 사용하게 되면 batch 테이블에 작업 내용이 자동으로 저장되기 때문에 작업 실행 이력을 추적하고 관리하기에도 용이하다고 판단했다.
@Configuration
@Slf4j
public class ExpirationDateUpdateJob {
private final JobRepository jobRepository;
private final PlatformTransactionManager transactionManager;
private final ShopItemRepository shopItemRepository;
private final ShopProductPriceRepository shopProductPriceRepository;
@Value("${product.discount.threshold.days}")
private int expirationThresholdDays;
@Value("${product.expiration.discountRatio}")
private int expirationDiscountRatio;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
public ExpirationDateUpdateJob(JobRepository jobRepository,
PlatformTransactionManager transactionManager,
ShopItemRepository shopItemRepository,
ShopProductPriceRepository shopProductPriceRepository) {
this.jobRepository = jobRepository;
this.transactionManager = transactionManager;
this.shopItemRepository = shopItemRepository;
this.shopProductPriceRepository = shopProductPriceRepository;
}
@Bean
public Job updateExpirationDateJob(Step updateExpirationDateStep) {
return new JobBuilder("updateExpirationDateJob", jobRepository)
.start(updateExpirationDateStep)
.build();
}
@Bean
public Step updateExpirationDateStep() {
return new StepBuilder("updateExpirationDateStep", jobRepository)
.tasklet((contribution, chunkContext) -> {
List<ShopItem> expiringItems = shopItemRepository.checkExpirationDate(expirationThresholdDays);
List<String> itemIdsToUpdate = expiringItems.stream()
.map(ShopItem::getId)
.collect(Collectors.toList());
if (!itemIdsToUpdate.isEmpty()) {
shopProductPriceRepository.updateDiscountForItems(itemIdsToUpdate, expirationDiscountRatio);
log.info("Updated discount for {} items", itemIdsToUpdate.size());
for (ShopItem item : expiringItems) {
log.info("Updated item - Name: {}, Distribution Period: {}",
item.getName(),
dateFormat.format(item.getDistributionPeriod()));
}
} else {
log.info("No items to update");
}
return RepeatStatus.FINISHED;
}, transactionManager)
.build();
}
}
ExpirationDateUpdateJob:
Spring Batch Job을 정의하는 설정 클래스다.
@Configuration 어노테이션으로 Spring 설정 클래스임을 표시한다.
Job과 Step을 Bean으로 정의한다.
updateExpirationDateStep에서 실제 작업을 수행한다:
유통기한 임박 상품 조회
해당 상품들의 할인율 업데이트
업데이트된 상품 정보 로그 기록
@Component
@Slf4j
@RequiredArgsConstructor
public class ExpirationDateUpdateScheduler {
private final JobLauncher jobLauncher;
private final Job updateExpirationDateJob;
//@Scheduled(cron = "0 * * * * ?") // 매 분마다 실행
//@Scheduled(cron = "0 0 0 * * ?") // 매일 0시마다 실행
@Scheduled(cron = "0 0 * * * ?") // 매 시간 0분마다 실행
public void runUpdateExpirationDateJob() {
try {
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.toJobParameters();
jobLauncher.run(updateExpirationDateJob, jobParameters);
} catch (Exception e) {
log.error("Error occurred while running update expiration date job: ", e);
}
}
}
@Scheduled 어노테이션으로 주기적 Job 실행을 담당하는 스케줄러다.
@Component 어노테이션으로 Spring Bean 등록
JobLauncher로 ExpirationDateUpdateJob 실행
현재 매 시간 0분마다 Job 실행 설정