spring-batchでメタデータとstepで異なるTransactionManagerを使う

spring-batchで2つ以上のデータソースを使う場合、step定義作成時に```StepBuilderHelper#transactionManager```でトランザクションマネージャを指定する。

以下は設定例。

```java
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.batch.BatchDataSource;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@SpringBootApplication
@EnableBatchProcessing
public class Application {
	@Bean
	@BatchDataSource
	public DataSource springBatchDs() {
		// 1
		return DataSourceBuilder
				.create()
				.url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
				.username("sa")
				.password("")
				.build();
	}
	
	@Bean
	@Primary
	public DataSource primaryDs() {
		// 2
		return DataSourceBuilder
				.create()
				.url("jdbc:oracle:thin:system/oracle@localhost:11521/XEPDB1")
				.username("system")
				.password("oracle")
				.build();
	}
	
	
	@Bean
	public Job job(JobBuilderFactory jobs, Step s1) {
		return jobs.get("myJob")
				.incrementer(new RunIdIncrementer())
				.start(s1)
				.build();
	}
	
	@Bean("stepTransactionManager")
	public PlatformTransactionManager stepTransactionManager(DataSource primaryDs) {
		// 3
		return new DataSourceTransactionManager(primaryDs);
	}
	
	@Bean
	public Step step1(StepBuilderFactory steps
			,DataSource dataSource
			// 4
			,@Qualifier("stepTransactionManager") PlatformTransactionManager transactionManager) {
		ItemReader<Integer> reader = new ListItemReader<Integer>(
				IntStream.range(1, 1001).boxed().collect(Collectors.toList()));

		JdbcTemplate jdbc = new JdbcTemplate(dataSource);
		
		TaskletStep build = steps.get("step1")
				// 5
				.transactionManager(transactionManager)
				.<Integer, Integer>chunk(10)
				.reader(reader)
				.writer(list -> {
					list.forEach(s -> {
						jdbc.update("update aaa set user_id = user_id + 1");
					});
				})
				.build();
		return build;
	}
	
	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run(args);
	}
}
```

1. サンプルとして、メタデータの保存先はH2を使用する。終了時に消えるので、事実上メタデータは永続化しない。
2. メインのデータソース。
3. step処理用のトランザクションマネージャ。メインのデータソースを使用する。メインのデータソースに```@Primary```がついてるので、このメソッドの引数にはそちらのデータソースが来る。
4. ```@Qualifier```で上で作成したトランザクションマネージャを得る。```@Qualifier```が無いと、spring-batchの```SimpleBatchConfiguration```が内部的に作成する```transactionManager```が入ってくる。
5. step定義作成時に上で作成したトランザクションマネージャを指定する。これでメタデータ処理とstep処理とで異なるトランザクションマネージャが使用される。