요약
SpringBatch에선 ItemReader, ItemProcessor, ItemWriter을 사용해 배치처리를 chunkSize 단위로 수행합니다. 그런데 으레 사용되는 방식과는 다른 구현체가 필요해져 ItemWriter를 직접 구현한 커스텀 클래스를 만들어 사용했습니다.
Spring Batch의 Step에선 reader, processor, writer가 각자의 역할을 수행하는데, 이 때 processor는 1개의 아이템을 받아 처리 후 그 1개의 아이템을 writer가 받아, 청크 사이즈만큼 모이면 write(List) 메서드를 호출해 배치 인서트로 저장하는 구조입니다.
그런데 제가 만든 processor는 1개의 아이템을 받아 n개의 엔티티를 생성하는 로직이었습니다(1쌍의 [지역, 날씨 데이터 종류]을 받아 n개의 날씨정보 entity를 생성). 그래서 writer가 1개가 아니라 n개를 받아야하는 상황인 것입니다. 이렇게 되면 writer에서 엔티티를 저장할 때 중첩리스트를 받는 write(List<List>)가 이뤄지기 때문에, 기존 JdbcBatchItemWriter로는 저장할 수 없었습니다.
그래서 ItemWriter가 아닌 ItemWriter<List>를 구현하는 클래스를 만들고, write 메서드를 오버라이드해서, 인자로 받은 중첩리스트를 flattening하고, 생성자로 주입된 ItemWriter의 write 메서드를 호출함으로써 BatchInsert를 수행했습니다.
🔗 중첩 리스트를 받아 배치 인서트를 수행하는 커스텀 Writer 클래스
@RequiredArgsConstructor
@Slf4j
public class JdbcBatchListItemWriter<T> implements ItemWriter<List<T>>, ItemStream, InitializingBean {
private final JdbcBatchItemWriter<T> delegate;
@Override
public void write(List<? extends List<T>> items) throws Exception {
final List<T> flattenedList = new ArrayList<>();
for (final List<T> list : items) {
flattenedList.addAll(list);
}
log.info("Executing batch with " + flattenedList.size() + " items in listItemWriter");
log.info(flattenedList.toString());
delegate.write(flattenedList);
}
🔗 일반 ItemWriter를 주입받아, 리스트를 저장하는 ItemWriter를 생성
@Bean(JOB_NAME + "_loadInfoWriter")
@StepScope
public JdbcBatchListItemWriter<WeatherInfo> loadInfoWriter() {
log.info("********** loadInfoWriter Created");
String sql = "insert into weather_info(base_time, fcst_time, region, data_type, val) values(:baseTime, :fcstTime, :weatherRegion, :weatherDataType, :value)";
JdbcBatchItemWriter<WeatherInfo> itemWriter = new JdbcBatchItemWriterBuilder<WeatherInfo>()
.dataSource(dataSource)
.itemSqlParameterSourceProvider(loadInfoItemSqlParameterSourceProvider)
.sql(sql)
.build();
itemWriter.afterPropertiesSet();
return new JdbcBatchListItemWriter<>(itemWriter);
}