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


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

以下は設定例。

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処理とで異なるトランザクションマネージャが使用される。