Spring Batchでtsvファイルをバッチ処理でDBに取り込む

#はじめに

tsvファイルをresouse配下に置き、SpringBootのバッチ処理でPostgresのテーブルに取り込みを行う機能を作成したい。

#環境

- macOS Big Sur 11.2.3
- Java8
- SpringBoot Maven
- PostgreSQL

#pom.xml
Spring Batchを依存性に追加する

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
</dependency>
```

#テーブル作成

src/main/resources/schema-all.sql

```sql
DROP TABLE if EXISTS items;

create table items (
  id serial not null
, name character varying(255)
, condition integer
, category integer
, brand character varying(255)
, price double PRECISION
, shipping integer
, description text
, constraint items_PKC primary key (id)
);
```

Spring Bootでは、src/main/resources直下にschema.sqlやdata.sqlを配備することで、起動時にDDLやDMLを自動で実行してくれます。

　また、-allをつけることでどのプラットフォームでも（OracleやMySQLなど）実行されるようになります。

#ビジネスクラスの作成
TSVファイルの1レコードに対応するクラスを作成します。

```java
public class Item {
    private Integer id;
    private String name;
    private Integer condition;
    private Integer category;
    private String brand;
    private Integer price;
    private Integer shipping;
    private String description;
```

#バッジジョブをまとめる
@EnableBatchProcessingアノテーションをつけることで、Bean（Springが管理しているインスタンス）が使えるようになります。

```java
@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
	
    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;
    
    // tag::readerwriterprocessor[]
    @Bean
    public FlatFileItemReader<Item> reader() {
    	//ItemReaderを生成します。
    	//demo.tsvを探し出し、CSVファイル内の1行ずつをItemオブジェクトに変換します。
        return new FlatFileItemReaderBuilder<Item>()
            .name("ItemReader")
            .resource(new ClassPathResource("demo.tsv"))
            .linesToSkip(1) //ヘッダーをスキップ
            .delimited()
            .delimiter("\t") //タブ区切りを指定)
            .names(new String[]{"id","name","condition","category","brand","price","shipping","description"})
            .fieldSetMapper(new BeanWrapperFieldSetMapper<Item>() {{
            	setTargetType(Item.class);
            }})
            .build();
    }

    @Bean
    public JdbcBatchItemWriter<Item> writer(DataSource dataSource) {
        return new JdbcBatchItemWriterBuilder<Item>()
            .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
            .sql("INSERT INTO items (id,name,condition,category,brand,price,shipping,description) "
            		+ " VALUES (:id, :name, :condition, :category, :brand, :price, :shipping, :description )")
            .dataSource(dataSource)
            .build();
    }
 // tag::jobstep[]
    @Bean
    public Job importItemJob(Step step1) {
        return jobBuilderFactory.get("importItemJob") //Job名を指定
            .incrementer(new RunIdIncrementer())
            .flow(step1)	//実行するStepを指定
            .end()
            .build();
    }

    @Bean
    public Step step1(JdbcBatchItemWriter<Item> writer) {
    	//inputはItemReader<Item>で、outputがItemWriter<Item>
    	//10レコードずつ書き込む
        return stepBuilderFactory.get("step1") //Step名を指定
            .<Item, Item> chunk(10)
            .reader(reader())
            .writer(writer)
            .build();
    }
    // end::jobstep[]
```

readerはItemReaderを生成します。これは、demo.tsvを探し出し、TSVファイル内の1行ずつをItemオブジェクトに変換します。

writerはItemWriterを生成します。データベース内のテーブルに出力内容を書き込んでいます。

#バッチの起動設定

```yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/DB名
    username: user
    password: pass
    driver-class-name: org.postgresql.Driver
  batch:
    initialize-schema: always
    job:
      enabled: false
```
"initialize-schema: always"
SpringBatchが使用するテーブルのDDLを実行するための設定。デフォルトではembeddedで、PostgreSQLを使用する場合はDDLが実行されないので、明示的に変更しておく。

"enabled: false "
デフォルトではtrueでSpring起動時にJobが実行されるが、起動時はJobParametersがないため起動時には実行できない。そのため明示的に変更しておく。