Spring Batchで複数ファイルをItemReaderに使う
0. Spring Batchと格闘中です
現職では、自力でバッチアプリを書いていて、なぜか思いのほかJavaと格闘している。
利用シーンとしては、CSV提供されるマスタデータを複数のテーブルに格納する、というありがちな奴なんだけど、このCSVが一本ではなく複数ある。
ということなので、最初はタスクレットモデルで複数ファイルを一気に扱う方式で実装していたけれど、メモリ使用量とかの問題でチャンクモデルで実装しなおすことに。
と、ここで問題が。
普通のFlatFileItemReaderでは複数のファイルを同時に処理できない。
そこでMultiResourceItemReaderの出番です。
1. MultiResourceItemReader
Reads items from multiple resources sequentially
(複数のリソースから項目を順番に読み取る) とあるとおり、処理を移譲されたItemReaderに順番にResourceを渡す。
ソース全体はhttps://github.com/f97one/MultiFileItemReaderBatchExを参照いただくとして、肝になりそうな部分を抜粋してみる。
ここでは、同じ書式の複数のファイルから二つのテーブルにデータを振り分ける処理を書いてみた。
@Bean
fun directoryOrgReader(orgReader: FlatFileItemReader<OrgFile>): MultiResourceItemReader<OrgFile> {
// Resourceを配列として返す
// 渡したくないファイルがあるならここでフィルタしておく
val dir = Path.of("C:", "work", "readtest")
val dirFiles = Files.list(dir).collect(Collectors.toList())
val csvResList = mutableListOf<Resource>()
for (p in dirFiles) {
csvResList.add(FileSystemResource(p)) // プロジェクト外のファイルなので FileSystemResource を使う
}
val reader = MultiResourceItemReader<OrgFile>()
reader.setResources(csvResList.toTypedArray())
reader.setDelegate(orgReader) // ここに入っているBeanで実際に処理される
return reader
}
@Bean
fun orgReader(): FlatFileItemReader<OrgFile> {
val mapper = DefaultLineMapper<OrgFile>()
val delimitedLineTokenizer = DelimitedLineTokenizer()
delimitedLineTokenizer.setNames("id", "subject", "itemType")
mapper.setLineTokenizer(delimitedLineTokenizer)
val fieldSetMapper = BeanWrapperFieldSetMapper<OrgFile>()
fieldSetMapper.setTargetType(OrgFile::class.java)
mapper.setFieldSetMapper(fieldSetMapper)
// 本来のFlatFileItemReaderには処理するResourceを指定する必要があるが、
// delegateしてくるMultiResourceItemReaderからResourceを供給されるので
// ここには何も書かなくてよい
return FlatFileItemReaderBuilder<OrgFile>()
.name("orgFileReader")
.linesToSkip(1)
.encoding("windows-31j")
.lineMapper(mapper)
.build()
}
@Bean
fun step1(directoryOrgReader: MultiResourceItemReader<OrgFile>, orgItemWriter: MultiTblItemWriter): Step {
// ここではItemProcessorは定義していないが、必要に応じて追加しよう
return stepBuilderFactory.get("step1")
.chunk<OrgFile, OrgFile>(10)
.reader(directoryOrgReader) // MultiResourceItemReaderのほうをreaderにする
.writer(orgItemWriter) // MultiTableItemWriterはItemWriterを実装したクラス
.build()
}
ItemWriterについてはよくあるやつなので、GitHubのほうを観てもらえば雰囲気はわかると思う。
え? 読み込ませるファイルに順序がある? そんなときはComapratorを実装すればいい。
@Bean
fun directoryOrgReader(orgReader: FlatFileItemReader<OrgFile>): MultiResourceItemReader<OrgFile> {
// Resourceを配列として返す
// 渡したくないファイルがあるならここでフィルタしておく
val dir = Path.of("C:", "work", "readtest")
val dirFiles = Files.list(dir).collect(Collectors.toList())
val csvResList = mutableListOf<Resource>()
for (p in dirFiles) {
csvResList.add(FileSystemResource(p)) // プロジェクト外のファイルなので FileSystemResource を使う
}
val reader = MultiResourceItemReader<OrgFile>()
reader.setResources(csvResList.toTypedArray())
reader.setDelegate(orgReader) // ここに入っているBeanで実際に処理される
reader.setComparator { o1, o2 ->
// o1, o2 とも nullable な Resource のインスタンスなので、
// getFile() なり getFileName() なりして比較結果を
// Int で返してやろう
return 0
}
return reader
}
« CSRを楽に作る | トップページ | Spring + Thymeleafで検索 + ページング + ソートを同時にやる件 »
コメント
« CSRを楽に作る | トップページ | Spring + Thymeleafで検索 + ページング + ソートを同時にやる件 »
こんにちは。
じっくり勉強させていただきます!
投稿: 師子乃 | 2020年11月14日 (土) 14時29分