SpringBatchはじめての

## 説明
Spring Batchを使って初めて作ったときのまとめの話です。
(開発環境は執筆時点で最近のものにました)

## 環境
| モノ     | バージョン                        |
|:-----|:-----------------------|
| OS   |Windows10 Home（64bit） | 
| IDE| Pleiades All in One Eclipse Windows 64bit Standard Edition | 
| Java | AdoptOpenJDK OpenJDK 11 (LTS) / HotSpot |
| その他 | Spring Boot 2.5.2 |
| その他 | ビルドツールはMaven |

## 環境構築
* Eclipseをダウンロード→任意のフォルダに展開
* AdoptOpenJDKのダウンロードとインストール
* 環境変数に[JAVA_HOME]を設定しておく
* Eclipseを起動する(eclipse.exe -clean.cmd)
* ワークスペースを作る(デフォルト[../workspace]で起動)

## プロジェクトを作成する
* [新規 Spring スターター・プロジェクトの作成]をクリック
* デフォルトで[次へ]
* 依存関係で以下を追加しておく
	* Lombok
	* Spring Batch
	* MyBatis Framework
	* PostgreSQL Driver
* [次へ]でサイト情報が出るのでそのまま[完了]

## 初期状態のファイル
<details><summary>tree /fの結果</summary><div>

```
C:\PLEIADES\WORKSPACE\DEMO
│  .classpath
│  .gitignore
│  .project
│  HELP.md
│  mvnw
│  mvnw.cmd
│  pom.xml
│
├─.mvn
│  └─wrapper
│          maven-wrapper.jar
│          maven-wrapper.properties
│          MavenWrapperDownloader.java
│
├─.settings
│      org.eclipse.core.resources.prefs
│      org.eclipse.jdt.core.prefs
│      org.eclipse.m2e.core.prefs
│
├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─example
│  │  │          └─demo
│  │  │                  DemoApplication.java
│  │  │
│  │  └─resources
│  │          application.properties
│  │
│  └─test
│      └─java
│          └─com
│              └─example
│                  └─demo
│                          DemoApplicationTests.java
│
└─target
    ├─classes
    └─test-classes

```

</div>

### とりあえずパッケージを作成する

```
mvnw.cmd clean package
```

### とりあえずエラーになるっぽい

```
[INFO] Results:
[INFO]
[ERROR] Errors:
[ERROR]   DemoApplicationTests.contextLoads ≫ IllegalState Failed to load ApplicationCon...
[INFO]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
```

### ログの中盤エラーの説明がありました

```
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
```
| エラー | 訳 |
|:-----|:-----------------------|
| Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.Reason: Failed to determine a suitable driver class | データソースの構成に失敗しました：「url」属性が指定されておらず、埋め込みデータソースを構成できませんでした。理由：適切なドライバークラスを決定できませんでした|

testを実行しようとして、Spring Batchのメタデータテーブルにアクセスしようとしたけど、
データソースが構成されていないので出るエラーのようです。ここではテストをしないので、
テストを除外するコマンドで、パッケージができることを確認します
（テストクラスを消してしまってもよいそうです）

```
mvnw.cmd clean package -DskipTests (テストコードを実行しない)
mvnw.cmd clean package -Dmaven.test.skip (テストコードのコンパイル、およびテストの実行をスキップ)
```
どちらでも同じ結果でした
mvnのコマンドについては、
こちら [よく使うMavenコマンド集](https://qiita.com/KevinFQ/items/e8363ad6123713815e68)を参考にさせてもらいました。

```
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
```

### 実行する

```
java -jar .\target\demo-0.0.1-SNAPSHOT.jar
```

### 結局同じエラーが出ますがここでは良しとしときます

```
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
        If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
        If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
```

## Batchにします
ここまではSpringBootのデモなので、ここからSpringBatchで動くようにコードを追加していきます
単純な動作確認が目的なのでItemReaderなどは用意せず、単純なタスクレットを作成します
ジョブ：ステップ：タスクレットを１：１：１にします

### コードの追加
[バッチサービスの作成(https://spring.pleiades.io/guides/gs/batch-processing/)](https://spring.pleiades.io/guides/gs/batch-processing/) を参考にやっていきます

```DemoBatchConfig.java
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class DemoBatchConfig {

	@Autowired
	public JobBuilderFactory jobBuilderFactory;

	@Autowired
	public StepBuilderFactory stepBuilderFactory;

	@Autowired
	private DemoTasklet task1;

	@Bean
	public Step step1() {
		return stepBuilderFactory.get("step1").tasklet(task1).build();
	}

	@Bean
	public Job job1() {
		return jobBuilderFactory.get("job1").start(step1()).build();
	}
}
```

```DemoTasklet.java
@RequiredArgsConstructor
@Component
public class DemoTasklet implements Tasklet {

	@Override
	public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
		System.out.println("called DemoTasklet");
		return RepeatStatus.FINISHED;
	}
}
```

```diff_java:DemoApplication.java
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
-		SpringApplication.run(DemoApplication.class, args);
+		System.exit(SpringApplication.exit(SpringApplication.run(DemoApplication.class, args)));
	}

``` 
### 実行する
先のエラーを解消する必要があります
application.propertiesにデータソースの定義が必要だと思ったんですが、
参考にしている[バッチサービスの作成](https://spring.pleiades.io/guides/gs/batch-processing/)のファイルを確認したところ
何も記載されていませんでした。
よくよく確認したところHyperSQL データベースを使っているようで、
Spring Batchのメタデータテーブルの生成や登録はメモリ上で行われているようです。（終わった後は見えない）
ここではバッチ処理が動くことを確認したいので、同じ設定になるようにpom.xmlを編集します。
(myBatisもPostgreSQLも使ってないので消します)

```diff:pom.xml
-		<dependency>
-			<groupId>org.mybatis.spring.boot</groupId>
-			<artifactId>mybatis-spring-boot-starter</artifactId>
-			<version>2.2.0</version>
-		</dependency>
-		<dependency>
-			<groupId>org.postgresql</groupId>
-			<artifactId>postgresql</artifactId>
-			<scope>runtime</scope>
-		</dependency>
+		<dependency>
+			<groupId>org.hsqldb</groupId>
+			<artifactId>hsqldb</artifactId>
+			<scope>runtime</scope>
+		</dependency>
```

### パッケージを作成して実行する
* テストをスキップしなくてもエラーにならず、実行エラーも出ませんでした
最初からこうすればよかった

```
mvnw.cmd clean package
java -jar .\target\demo-0.0.1-SNAPSHOT.jar
```

* 実行結果

``` 
called DemoTasklet
```

## メタテーブルを作って動かしたいのですが
「Spring Batch メタテーブル」で検索すると、作らない使い方を調べるサイトがたくさんヒットするのですが、
いったん使ってみてからジャッジしたいので、まずはメタテーブルを作りたい
いざ作り方を調べるとこれといったものが見つからないので困っていたが
やはり[公式DDL スクリプトの例](https://spring.pleiades.io/spring-batch/docs/current/reference/html/schema-appendix.html#migrationDDLScripts)
>Spring Batch コア JAR ファイルには、多数のデータベースプラットフォームのリレーショナルテーブルを作成するサンプルスクリプトが含まれています（これらは、ジョブリポジトリファクトリ Bean または同等のネームスペースによって自動検出されます）。これらのスクリプトは、そのまま使用することも、必要に応じて追加のインデックスと制約を使用して変更することもできます。ファイル名の形式は schema-*.sql です。ここで、"*" はターゲットデータベースプラットフォームの短縮名です。スクリプトは、パッケージ org.springframework.batch.core にあります。
>

とあるので、Eclipseのパッケージエクスプローラから
[Maven依存関係]->[spring-bach-core-4.3.3.jar]を右クリック->[Zipエディターで開く]で開いた中の
org/springframerowk/batch/coreの下に、schema-postgresql.sqlとschema-drop-postgresql.sqlを見つけた

#### このSQLはどうやって動かすのですか
勘違いしてたのに気づくのが遅かったのですが、「Spring Bootの使い方」として、
[基本的な SQL スクリプトを使用してデータベースを初期化する](https://spring.pleiades.io/spring-boot/docs/current/reference/html/howto.html#howto.data-initialization.using-basic-sql-scripts)
というのがあるそうです。
>標準のルートクラスパスの場所（それぞれ schema.sql と data.sql）から SQL をロードします。さらに、Spring Boot は schema-${platform}.sql ファイルと data-${platform}.sql ファイル（存在する場合）を処理します。

どうも、resource配下にsqlファイルを置いておけば、Spring Bootの実行時に自動的に動かしてくれるものらしいです
でも、先ほど手に入れたのは「Spring Batch データベース」っていうメタデータのDDLであって、
Spring Bootが起動時にロードするsqlとは、取り扱うものが違うようなんですよね

と、いうことで先ほど手に入れたschema-postgresql.sqlで、予め用意してあったPostgreSQLのDBに
オブジェクトを作っていきます。

```sql:schema-postgresql.sql
-- Autogenerated: do not edit this file
CREATE TABLE BATCH_JOB_INSTANCE  (
	JOB_INSTANCE_ID BIGINT  NOT NULL PRIMARY KEY ,
	VERSION BIGINT 
以降省略
```

```
testdb=# \dt batch*
                      リレーションの一覧
 スキーマ |             名前             |    型    |  所有者
----------+------------------------------+----------+----------
 public   | batch_job_execution          | テーブル | postgres
 public   | batch_job_execution_context  | テーブル | postgres
 public   | batch_job_execution_params   | テーブル | postgres
 public   | batch_job_instance           | テーブル | postgres
 public   | batch_step_execution         | テーブル | postgres
 public   | batch_step_execution_context | テーブル | postgres
(6 行)
```

### データソースを設定する

```diff:application.properties
+ spring.datasource.driver-class-name=org.postgresql.Driver
+ spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
+ spring.datasource.username=postgres
+ spring.datasource.password=postgres
```

### 依存関係を編集する
PostgreSQLを使うようにします

```diff:pom.xml
+		<dependency>
+			<groupId>org.postgresql</groupId>
+			<artifactId>postgresql</artifactId>
+			<scope>runtime</scope>
+		</dependency>
-		<dependency>
-			<groupId>org.hsqldb</groupId>
-			<artifactId>hsqldb</artifactId>
-			<scope>runtime</scope>
-		</dependency>
```

### 実行する
```
mvnw.cmd clean package -Dmaven.test.skip
java -jar .\target\demo-0.0.1-SNAPSHOT.jar
```
仕込んだメッセージが出たので、実行できました。

```sql
testdb=# SELECT job_execution_id, status, exit_code, exit_message FROM batch_job_execution;
 job_execution_id |  status   | exit_code | exit_message
------------------+-----------+-----------+--------------
                1 | COMPLETED | COMPLETED |
```

### もう一回実行すると仕込んだメッセージが出なかった

```sql
testdb=# SELECT job_execution_id, status, exit_code, exit_message FROM batch_job_execution;
 job_execution_id |  status   | exit_code |                           exit_message
------------------+-----------+-----------+------------------------------------------------------------------
                1 | COMPLETED | COMPLETED |
                2 | COMPLETED | NOOP      | All steps already completed or no steps configured for this job.
```
2回目の実行後のexit_codeがNOOP(処理を行わなかったジョブ)でした。
All steps already completed or no steps configured for this job.
(すべてのステップがすでに完了しているか、このジョブにステップが構成されていません。)
とのこと
気軽に再実行できると思ってたら、どうも同じパラメータの場合は同一ジョブとみなして
再起動できないっぽいです
なので、RunIdIncrementerというのを使って、同じパラメータのジョブを動かせるようにしたいと
思います

```diff_java:DemoBatchConfig.java
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class DemoBatchConfig {

	@Autowired
	public JobBuilderFactory jobBuilderFactory;

	@Autowired
	public StepBuilderFactory stepBuilderFactory;

	@Autowired
	private DemoTasklet task1;

	@Bean
	public Step step1() {
		return stepBuilderFactory.get("step1").tasklet(task1).build();
	}

	@Bean
	public Job job1() {
-		return jobBuilderFactory.get("job1").start(step1()).build();
+		return jobBuilderFactory.get("job1").incrementer(new RunIdIncrementer()).start(step1()).build();
	}
}
```

で、一旦メタデータのテーブルを再作成してから２回実行すると２回目も動きました。

```sql
testdb=# SELECT job_execution_id, status, exit_code, exit_message FROM batch_job_execution;
 job_execution_id |  status   | exit_code | exit_message
------------------+-----------+-----------+--------------
                1 | COMPLETED | COMPLETED |
                2 | COMPLETED | COMPLETED |
```

ログを見ていると微妙に変わってました。
parameters: [{run.id=xx}]が付くようになったので、
再実行ができるようになったようです。

```
◇修正前
INFO 796 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{}]

◇修正後(1回目と2回目抜粋）
INFO 112 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{run.id=1}]
INFO 621 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [SimpleJob: [name=job1]] launched with the following parameters: [{run.id=2}]
```

## 最後に
Spring Batchを使って初めて作ったときに気になった所をまとめてみました。
難しいことはできていませんが、まずは動くようにできたのでここまでにします。