<Spring Batch> API 호출해서 DB에 저장하기(with JPA)
by BFine2021/02/25 - [개발공부/Spring ] - 예제 프로그램 따라하기
저번에 예제 프로그램을 따라해보고 이정도면 크게 어렵지 않네? 라는 착각을 하고 있었다...
예제를 활용해서 간단한거 만들어봤는데 오류가 오류가... 쉴틈없이 쏟아졌다.. Spring Batch 쉽지않다
가. 고민하기
a. 뭘 만들까?
- 일단 API 요청을 통해 JSON을 받아서 그걸 활용해서 만들어 보고 싶었다.
- 처음에는 Tistory API를 써보려 했는데 보안 정책이 강화되어 implict 방식이 없어졌다
- 고민하던중 예전에 공부할때 쓰던 영화 API가 생각나서 이걸로 하기로 결정했다.
=> www.kobis.or.kr/kobisopenapi/homepg/apiservice/searchServiceInfo.do
b. 동작
- 단순하다 순서는
1. 영화 API를 호출 (JSON)
2. 오늘 상영객수 * 만이천원(영화 관람료.. 많이 올랐네)
3. 일매출액을 DB에 저장 (JPA)
나. Step
a. reader
- API를 호출해야하기 때문에 상속받아 구현했다.
- WebClient를 사용해서 API를 호출해 보았다. 예전에 한번 써봤는데 기억이 나지 않아서 오류가 많이 생겼다.
=> JSON 응답일 경우 Jackson 관련 설정을 꼭 해줘야한다.
@RequiredArgsConstructor
public class BoxOfficeItemReader implements ItemReader<BoxOfficeResult> {
@Override
public BoxOfficeResult read() {
ExchangeStrategies strategies = ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs()
.jackson2JsonEncoder
(new Jackson2JsonEncoder(new ObjectMapper(),
MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer
.defaultCodecs()
.jackson2JsonDecoder
(new Jackson2JsonDecoder(
new ObjectMapper(),
MediaType.APPLICATION_JSON));
}).build();
WebClient webClient = WebClient.builder()
.exchangeStrategies(strategies).build();
Mono<InputData> boxOfficeResultMono = webClient.get()
.uri(BASE+BOXOFFICE+API_KEY+targetDt)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(InputData.class)
.log();
InputData result = boxOfficeResultMono.block();
return result.getBoxOfficeResult();
}
b. processor
- 로직을 처리하자
public class BoxOfficeItemProcessor implements ItemProcessor<BoxOfficeResult, List<OutputData>> {
private final int price = 12000;
@Override
public List<OutputData> process(BoxOfficeResult input){
LocalDate targetDate = LocalDate.now();
List<DailyBoxOfficeList> dailyBoxOfficeList = input.getDailyBoxOfficeList();
return dailyBoxOfficeList.stream().map(i -> OutputData.builder()
.movieName(i.getMovieNm())
.targetDate(targetDate)
.totalRevenue(Integer.parseInt(i.getAudiCnt()) * price)
.build())
.collect(Collectors.toList());
}
}
c. writer
- 제일 고생한 부분이다 .. 처음에는 JpaItemBuilder를 활용해서 예제처럼 해보려고 했지만 문제가 생겼다.
=> Processor 에서 List를 리턴하기 때문에 Not an entity[class java.util.ArrayList] 발생했다.
- 참고 : jojoldu.tistory.com/140
- 위 블로그에 잘 정리 되어있는데 결국 writer도 상속받아서 처리해야 한다.
- 만들고 나서 보니 An EntityManagerFactory is required 오류가 발생한다.
=> afterPropertiesSet 에서 entityManagerFactory가 set 되기전에 호출하기 때문에 발생한다.
@AllArgsConstructor
public class BoxOfficeItemWriter<T> extends JpaItemWriter<List<T>> {
private final JpaItemWriter<T> jpaItemWriter;
@Override
@Transactional
public void write(List<? extends List<T>> items) {
List<T> collect = new ArrayList<>();
for(List<T> list : items){
collect.addAll(list);
}
jpaItemWriter.write(collect);
}
@Override
public void afterPropertiesSet(){
// An EntityManagerFactory is required 방지
}
}
다. Config
a. Batch
- 공들인 시간에 비해 큰 틀은 예제와 거의 비슷하다...
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class BatchConfig {
public final JobBuilderFactory jobBuilderFactory;
public final StepBuilderFactory stepBuilderFactory;
public final EntityManagerFactory entityManagerFactory;
@Bean
public BoxOfficeItemReader reader(){
return new BoxOfficeItemReader();
}
@Bean
public BoxOfficeItemProcessor processor(){
return new BoxOfficeItemProcessor();
}
@Bean
public BoxOfficeItemWriter<OutputData> writer(){
JpaItemWriter<OutputData> writer = new JpaItemWriter<>();
writer.setEntityManagerFactory(entityManagerFactory);
return new BoxOfficeItemWriter<>(writer);
}
@Bean
public Step step(){
return stepBuilderFactory.get("step")
.<BoxOfficeResult,List<OutputData>>chunk(1)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
@Bean
public Job job(JobCompleteListener jobCompleteListener,Step step){
return jobBuilderFactory.get("job")
.listener(jobCompleteListener)
.flow(step)
.end()
.build();
}
}
라. 테스트
a. 실행
- 여기까지 했으면 실행시 Batch 무한루프가 발생할 것이다.
=> 단순하다 Reader는 null이 아닐때까지 반복한다. DB나 파일에 끝이 있듯이 이것도 끝을 만들어 주어야한다.
- null 처리 이후에 다시 실행해보면..
b. DB 확인
- 데이터가 잘 입력되었는지 확인해 보자
- 어림잡아서 했지만 톱 순위의 일매출이 장난아니다.. 코로나 아니었으면 얼마일까...
- 내장으로 사용할때는 몰랐는데 DB에 Spring Batch 관련 스키마가 저장된다.
- 다음 포스팅에는 이부분을 정리하면서 dataSource 구성을 변경해봐야겠다.
- 내장 DB에 Spring Batch Table Schema 를 처리하도록 해봐야지
=> 저 테이블이 공용DB에 만들어진다면 상상만해도 끔직하다.
'공부 > Spring Batch' 카테고리의 다른 글
<Spring Batch> 3. 배치 테이블 Embedded DB(H2)로 따로 관리하기 (1) | 2021.09.27 |
---|---|
<Spring Batch> 2. JobRepository, Config Customizing (0) | 2021.09.22 |
<Spring Batch> 1. JobParameters, ExecutionContext (0) | 2021.09.21 |
<Spring Batch> 0. 스프링 배치란? (2) | 2021.09.19 |
<Spring Batch> 예제 프로그램 따라하기 (2) | 2021.02.25 |
블로그의 정보
57개월 BackEnd
BFine