SpringBatchで簡単なオンデマンドバッチを作成

# 要旨
簡単なSpringBatchを作成し、Webリクエストからバッチ起動する。

#### 環境
* Java SE 8 (jdk-8u181)
* Spring Boot 2.1.0

# 詳細手順
### プロジェクト作成
SpringBatchを使用するため、依存関係に`spring-boot-starter-batch`を加える。
また、Job管理にDBを使用するため、適当なJDBCも加える。

```xml:pom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <scope>runtime</scope>
</dependency>
```

### バッチ作成
**基本的に[こちら](https://qiita.com/toto1310/items/19d2e0dcc7c2154f7b00)を参考に作成**

#### バッチ実処理

`@Service`として適当な処理クラスを作成

```Java
@Service
public class MyBatchService {

	public void execute(Long id, String name, Date reqDate) throws InterruptedException {
		// 適当なバッチ処理
		Thread.sleep(5000);
	}
}
```

#### バッチ設定
バッチ処理の肝。今回は簡単に1ステップ(タスクレット)のジョブを1つ作成する。
Javaのコンフィグレーションクラスで設定する。

```Java
@Configuration
@EnableBatchProcessing //(1)
public class BatchConfig {

	@Autowired
	private JobBuilderFactory jobBuilderFactory; //(2)
	@Autowired
	private StepBuilderFactory stepBuilderFactory; //(2)

	@Autowired
	private MyBatchService service;

	@Bean
	public JobLauncher jobLauncher1(JobRepository jobRepository) { //(2),(3)
		ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
		// 同時実行数は3.それ以上はキュー待ちになる.
		taskExecutor.setCorePoolSize(3); //(4)
		// java.lang.IllegalStateException: ThreadPoolTaskExecutor not initialized
		taskExecutor.initialize(); //(5)

		SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
		jobLauncher.setJobRepository(jobRepository);
		jobLauncher.setTaskExecutor(taskExecutor);

		return jobLauncher;
	}

	@Bean
	public Job job(Step step1) { //(6)
		return jobBuilderFactory.get("batchjob")
					.incrementer(new RunIdIncrementer())
					.start(step1)
					.build();
	}

	@Bean
	public Step step1(Tasklet tasklet1) { //(6)
		return stepBuilderFactory.get("step1")
					.tasklet(tasklet1)
					.build();
	}

	@Bean
	@StepScope //(7)
	public Tasklet tasklet1( //(6)
			@Value("#{jobParameters['id']}") Long id, //(8)
			@Value("#{jobParameters['name']}") String name,
			@Value("#{jobParameters['reqDate']}") Date reqDate
			) {
        //(9)
		MethodInvokingTaskletAdapter tasklet = new MethodInvokingTaskletAdapter();
		tasklet.setTargetObject(service);
		tasklet.setTargetMethod("execute");
		tasklet.setArguments(new Object[] {id, name, reqDate});

		return tasklet;
	}
}
```

|||
|---|---|
|(1)|SpringBatchの設定であることを示す`@EnableBatchProcessing`を`@Configuration`とともに付与する。|
|(2)|いくつかのBeanはデフォルトでコンテキストに含まれているのでAutowiredすることができる。|
|(3)|Job実行環境の設定。|
|(4)|CorePoolSizeで同時実行数(スレッドを立てる数)を設定する。これを超える場合はキュー待ちになり、スレッドが空き次第順次実行される。|
|(5)|initializeしておかないと"java.lang.IllegalStateException: ThreadPoolTaskExecutor not initialized"という例外が発生する。|
|(6)|Job, Step, Taskletの設定。|
|(7)|実行時のパラメータを設定する場合は起動時にBeanを作成できないので`@StepScope`を付与しBean作成のタイミングを変更する。|
|(8)|jobParametersからオブジェクトを取り出す設定。|
|(9)|Invokeで実行する。|

#### バッチ呼び出し

```Java
@Service
public class MyServletService {

	@Autowired
	private JobLauncher jobLauncher1;
	@Autowired
	private Job job;

	public void request(Long id, String name) throws JobExecutionException {
		// (10)
		JobParameters params = new JobParametersBuilder()
								.addLong("id", id)
								.addString("name", name)
								.addDate("reqDate", Calendar.getInstance().getTime())
								.toJobParameters();

		jobLauncher1.run(job, params);
	}
}
```

|||
|---|---|
|(10)|Jobに渡すパラメータをJobParametersにまとめて渡す。|

#### 起動設定

application.ymlに設定を追加する。

```yml:application.yml
spring:
  datasource: # (11)
    url: jdbc:postgresql://localhost:5432/kurukuruz1
    username: u-s-e-r
    password: p-a-s-s
    driver-class-name: org.postgresql.Driver
  batch:
    initialize-schema: always # (12)
    job:
      enabled: false # (13)
```

|||
|---|---|
|(11)|使用するDBの接続情報を設定する。|
|(12)|SpringBatchが使用するテーブルのDDLを実行するための設定。デフォルトでは`embedded`で、PostgreSQLを使用する場合はDDLが実行されないので、明示的に変更しておく。|
|(13)|デフォルトでは`true`でSpring起動時にJobが実行されるが、起動時はJobParametersがないため起動時には実行できない。そのため明示的に変更しておく。|


# 参考サイト
[SpringBatch再入門 - 自分なりに使い方を整理してみた - Qiita](https://qiita.com/toto1310/items/19d2e0dcc7c2154f7b00)
[BLOG.IK.AM - 誤解しがちなThreadPoolTaskExecutorの設定](https://blog.ik.am/entries/443)
[Spring Batch サンプルコード (Java/Gradle) - Qoosky](https://www.qoosky.io/techs/3f60bee741)
[【Spring Batch】Jobの自動実行を行わない 指定したJob以外が自動で実行されてしまう時の制御法 - Qiita](https://qiita.com/s09ap161/items/742306c1230c0b29177d)
[コジオニルク - Spring Boot Batch で Hello World](http://www.kojion.com/posts/799)