1. 概要

Spring Batch は、堅牢なバッチアプリケーションを開発するための強力なフレームワークです。 前のチュートリアルでは、 SpringBatchを紹介しました。

このチュートリアルでは、前のチュートリアルに基づいて、Spring Bootを使用して基本的なバッチ駆動型アプリケーションをセットアップおよび作成する方法を学習します。

2. Mavenの依存関係

まず、spring-boot-starter-batchpom.xmlに追加しましょう。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
    <version>2.4.0</version>
</dependency>

org.hsqldb 依存関係も追加します。これは、 MavenCentralからも入手できます。

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.5.1</version>
    <scope>runtime</scope>
</dependency>

3. 単純なSpringBatchジョブの定義

CSVファイルからコーヒーリストをインポートし、カスタムプロセッサを使用して変換し、最終結果をメモリ内データベースに保存するジョブを作成します

3.1. 入門

アプリケーションのエントリポイントを定義することから始めましょう。

@SpringBootApplication
public class SpringBootBatchProcessingApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootBatchProcessingApplication.class, args);
    }
}

ご覧のとおり、これは標準のSpringBootアプリケーションです。 可能な場合はデフォルトの構成値を使用するため、アプリケーション構成プロパティの非常に軽いセットを使用します。

これらのプロパティは、 src / main / resources /application.propertiesファイルで定義します。

file.input=coffee-list.csv

このプロパティには、入力コーヒーリストの場所が含まれています。 各行には、ブランド、産地、およびコーヒーのいくつかの特徴が含まれています。

Blue Mountain,Jamaica,Fruity
Lavazza,Colombia,Strong
Folgers,America,Smokey

これから説明するように、これはフラットなCSVファイルです。つまり、Springは特別なカスタマイズなしで処理できます。

次に、SQLスクリプト schema-all.sql を追加して、データを格納するcoffeeテーブルを作成します。

DROP TABLE coffee IF EXISTS;

CREATE TABLE coffee  (
    coffee_id BIGINT IDENTITY NOT NULL PRIMARY KEY,
    brand VARCHAR(20),
    origin VARCHAR(20),
    characteristics VARCHAR(30)
);

便利なことに、SpringBootは起動時にこのスクリプトを自動的に実行します

3.2. コーヒードメインクラス

続いて、コーヒーアイテムを保持するための単純なドメインクラスが必要になります。

public class Coffee {

    private String brand;
    private String origin;
    private String characteristics;

    public Coffee(String brand, String origin, String characteristics) {
        this.brand = brand;
        this.origin = origin;
        this.characteristics = characteristics;
    }

    // getters and setters
}

前述のように、Coffeeオブジェクトには次の3つのプロパティが含まれています。

  • ブランド
  • 起源
  • いくつかの追加の特徴

4. ジョブ構成

次に、重要なコンポーネントであるジョブ構成について説明します。 ステップバイステップで構成を構築し、その過程で各部分を説明します。

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;
    
    @Value("${file.input}")
    private String fileInput;
    
    // ...
}

まず、標準のSpring @Configurationクラスから始めます。 次に、 @EnableBatchProcessing アノテーションをクラスに追加します。特に、これにより、ジョブをサポートする多くの便利なBeanにアクセスできるようになり、多くの作業を節約できます。

さらに、このアノテーションを使用すると、後でジョブ構成とジョブステップを構築するときに使用する2つの便利なファクトリにアクセスすることもできます。

初期構成の最後の部分には、前に宣言したfile.inputプロパティへの参照が含まれています。

4.1. 私たちの仕事のためのリーダーとライター

これで、構成でリーダーBeanを定義できます。

@Bean
public FlatFileItemReader reader() {
    return new FlatFileItemReaderBuilder().name("coffeeItemReader")
      .resource(new ClassPathResource(fileInput))
      .delimited()
      .names(new String[] { "brand", "origin", "characteristics" })
      .fieldSetMapper(new BeanWrapperFieldSetMapper() {{
          setTargetType(Coffee.class);
      }})
      .build();
}

つまり、上記で定義されたリーダーBeanは、coffee-list.csvというファイルを探し、各ラインアイテムをCoffeeオブジェクトに解析します。

同様に、ライターBeanを定義します。

@Bean
public JdbcBatchItemWriter writer(DataSource dataSource) {
    return new JdbcBatchItemWriterBuilder()
      .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
      .sql("INSERT INTO coffee (brand, origin, characteristics) VALUES (:brand, :origin, :characteristics)")
      .dataSource(dataSource)
      .build();
}

今回は、 Coffee オブジェクトのJavaBeanプロパティによって駆動される、単一のコーヒーアイテムをデータベースに挿入するために必要なSQLステートメントを含めます。 便利なことに、dataSourceは@EnableBatchProcessingアノテーションによって自動的に作成されます。

4.2. 私たちの仕事をまとめる

最後に、実際のジョブステップと構成を追加する必要があります。

@Bean
public Job importUserJob(JobCompletionNotificationListener listener, Step step1) {
    return jobBuilderFactory.get("importUserJob")
      .incrementer(new RunIdIncrementer())
      .listener(listener)
      .flow(step1)
      .end()
      .build();
}

@Bean
public Step step1(JdbcBatchItemWriter writer) {
    return stepBuilderFactory.get("step1")
      .<Coffee, Coffee> chunk(10)
      .reader(reader())
      .processor(processor())
      .writer(writer)
      .build();
}

@Bean
public CoffeeItemProcessor processor() {
    return new CoffeeItemProcessor();
}

ご覧のとおり、私たちの仕事は比較的単純で、step1メソッドで定義された1つのステップで構成されています。

このステップが何をしているのか見てみましょう:

  • まず、 branch(10)宣言を使用して、一度に最大10個のレコードを書き込むようにステップを構成します。
  • 次に、readerメソッドを使用して設定したリーダーBeanを使用してコーヒーデータを読み込みます。
  • 次に、各コーヒーアイテムをカスタムプロセッサに渡し、カスタムビジネスロジックを適用します
  • 最後に、前に見たライターを使用して、各コーヒーアイテムをデータベースに書き込みます。

一方、 importUserJob には、組み込みのRunIdIncrementerクラスを使用するIDを含むジョブ定義が含まれています。 ジョブが完了したときに通知を受け取るために使用するJobCompletionNotificationListenerも設定します

ジョブの構成を完了するために、各ステップをリストします(ただし、このジョブには1つのステップしかありません)。 これで、完全に構成されたジョブができました。

5. カスタムコーヒープロセッサー

以前にジョブ構成で定義したカスタムプロセッサを詳しく見てみましょう。

public class CoffeeItemProcessor implements ItemProcessor<Coffee, Coffee> {

    private static final Logger LOGGER = LoggerFactory.getLogger(CoffeeItemProcessor.class);

    @Override
    public Coffee process(final Coffee coffee) throws Exception {
        String brand = coffee.getBrand().toUpperCase();
        String origin = coffee.getOrigin().toUpperCase();
        String chracteristics = coffee.getCharacteristics().toUpperCase();

        Coffee transformedCoffee = new Coffee(brand, origin, chracteristics);
        LOGGER.info("Converting ( {} ) into ( {} )", coffee, transformedCoffee);

        return transformedCoffee;
    }
}

特に興味深いのは、 ItemProcessor インターフェイスは、ジョブの実行中に特定のビジネスロジックを適用するメカニズムを提供することです。

簡単にするために、 CoffeeItemProcessorを定義します。これは、入力Coffeeオブジェクトを受け取り、各プロパティを大文字に変換します

6. 仕事の完了

さらに、 JobCompletionNotificationListener を作成して、ジョブの終了時にフィードバックを提供します。

@Override
public void afterJob(JobExecution jobExecution) {
    if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
        LOGGER.info("!!! JOB FINISHED! Time to verify the results");

        String query = "SELECT brand, origin, characteristics FROM coffee";
        jdbcTemplate.query(query, (rs, row) -> new Coffee(rs.getString(1), rs.getString(2), rs.getString(3)))
          .forEach(coffee -> LOGGER.info("Found < {} > in the database.", coffee));
    }
}

上記の例では、 afterJob メソッドをオーバーライドして、ジョブが正常に完了したことを確認します。 さらに、簡単なクエリを実行して、各コーヒーアイテムがデータベースに正常に保存されたことを確認します

7. 私たちの仕事を実行する

仕事を実行するためのすべてが整ったので、ここに楽しい部分があります。 先に進んで、私たちの仕事を実行しましょう:

...
17:41:16.336 [main] INFO  c.b.b.JobCompletionNotificationListener -
  !!! JOB FINISHED! Time to verify the results
17:41:16.336 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=BLUE MOUNTAIN, origin=JAMAICA, characteristics=FRUITY] > in the database.
17:41:16.337 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=LAVAZZA, origin=COLOMBIA, characteristics=STRONG] > in the database.
17:41:16.337 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=FOLGERS, origin=AMERICA, characteristics=SMOKEY] > in the database.
...

ご覧のとおり、ジョブは正常に実行され、各コーヒーアイテムは期待どおりにデータベースに保存されました

8. 結論

この記事では、Spring Bootを使用して簡単なSpringBatchジョブを作成する方法を学びました。 まず、いくつかの基本的な構成を定義することから始めました。

次に、ファイルリーダーとデータベースライターを追加する方法を確認しました。 最後に、カスタム処理を適用する方法を確認し、ジョブが正常に実行されたことを確認しました。

いつものように、記事の完全なソースコードは、GitHubから入手できます。