電気ウナギ的○○

About Home

一番シンプルな Spring Batch 6 の Chunk 処理例

shinoda (2026年7月10日 18:21)
Chunk(チャンク)処理の基本通り、

  • ItemReader: Oracle Database からユーザー一覧(ID、名前、電話番号)を読み込む
  • ItemProcessor: 電話番号が携帯電話番号だったときは、番号の後ろに'(携帯)'とつける加工処理
  • ItemWriter: 読み込んだデータ(加工されたものもあり)を CSV ファイルに出力

という 3ステップ構成。
ただし、データが 1件もなかったときの処理や、CSV 出力に失敗したときの例外処理など、色々本物の業務バッチ処理としては足らない機能がたくさんあるので、それを勉強しながら少しずつ実装していくつもり。

まずは、そういう勉強の基盤になるソースということで、メモ代わりに貼っておく。
(プロジェクト名は 'BatchDb2Csv1')

とりあえず、

20260702_sample1.jpg

※「Oracle Database 21c Express Edition (XE) に A5 で接続」というエントリーで作成したデータ

こういうデータを Oracle Database から読み込んで、このエントリのバッチを走らせたら、

20260702_sample2.jpg

こういう CSV ファイルが吐かれます。


■application.properties

spring.application.name=BatchDB2CSV1

# Oracle 接続設定
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XEPDB1
spring.datasource.username=tester
spring.datasource.password=tester01
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver

# アプリ起動時にSpring Batchのジョブを自動実行する
spring.batch.job.enabled=true

# 【重要】Spring Batch等の管理テーブルを自動作成させる設定
spring.sql.init.mode=always

# Spring Boot DevToolsの自動再起動を無効化する(終了前に一時停止状態になるので)
spring.devtools.restart.enabled=false

YAML形式の方がお好みの方もいらっしゃるでしょうが、俺はこっちが好み。
どうも、半角スペースでインデント(字下げ)するのが(^^; Python 界隈の人は抵抗ないでしょうが(^^;;


■BatchDb2Csv1Application.java

package com.netandfield.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BatchDb2Csv1Application {

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

}


■Dto/TUserDto.java

package com.netandfield.test.Dto;

// DTOクラス・・・ではなく record(不変オブジェクト)で
public record TUserDto(Long user_id, String user_name, String user_phone_number) {}


■Config/BatchConfig.java

package com.netandfield.test.Config;

import javax.sql.DataSource;

import org.springframework.batch.core.job.Job;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.Step;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.infrastructure.item.ItemProcessor;
import org.springframework.batch.infrastructure.item.database.JdbcCursorItemReader;
import org.springframework.batch.infrastructure.item.database.builder.JdbcCursorItemReaderBuilder;
import org.springframework.batch.infrastructure.item.file.FlatFileItemWriter;
import org.springframework.batch.infrastructure.item.file.builder.FlatFileItemWriterBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.jdbc.core.DataClassRowMapper;
import org.springframework.transaction.PlatformTransactionManager;

import com.netandfield.test.Dto.TUserDto;

@Configuration
public class BatchConfig {

    private final JobRepository jobRepository;
    private final PlatformTransactionManager transactionManager;
    private final DataSource dataSource;

    // コンストラクタ注入
    public BatchConfig(JobRepository jobRepository, PlatformTransactionManager transactionManager, DataSource dataSource)
    {
        this.jobRepository = jobRepository;
        this.transactionManager = transactionManager;
        this.dataSource = dataSource;
    }

    // 1. Reader: Oracle DBからデータを読み込む
    @Bean
    public JdbcCursorItemReader<TUserDto> reader() {
        return new JdbcCursorItemReaderBuilder<TUserDto>()
                .dataSource(dataSource)
                .name("oracleItemReader")
                .sql("SELECT user_id, user_name, user_phone_number
FROM t_users ORDER BY user_id ASC")
                .rowMapper(new DataClassRowMapper<>(TUserDto.class)) // RowMapperでResultSet(検索結果オブジェクト)をDTOへマッピング
                .fetchSize(1000) // Oracleのパフォーマンス最適化(1,000行ずつ読込)
                .build();
    }

    // 2. Processor: データを加工する
    @Bean
    public ItemProcessor<TUserDto, TUserDto> processor() {
        return item -> {
            String phone = item.user_phone_number(); // DtoがRecordの場合のゲッター。通常のクラスなら getUser_phone_number()

            // 電話番号が null でなく、かつ 090 または 080 で始まる場合
            if (phone != null && (phone.startsWith("090") || phone.startsWith("080"))) {
                // 文字列を追加した新しいDtoを作成して返す
                // (DtoがRecordの場合はイミュータブル(不変)なので、このように新しくインスタンスを作る)
                return new TUserDto(
                        item.user_id(),
                        item.user_name(),
                        phone + "(携帯)"
                );
            }

            // 条件に当てはまらない場合は、加工せずにそのまま次に渡す
            return item;
        };
    }

    // 3. Writer: CSVファイルへ出力する
    @Bean
    public FlatFileItemWriter<TUserDto> writer() {
        return new FlatFileItemWriterBuilder<TUserDto>()
                .name("csvItemWriter")
                .resource(new FileSystemResource("C:\\work\\test\\t_users.csv"))
                .delimited() // 区切り文字(Delimited)を使ったフォーマットで書き出すことを指定
                .delimiter(",") // 区切り文字をカンマに設定(省略した場合もカンマ)
                .names("user_id", "user_name", "user_phone_number") // 出力するフィールド名
                .headerCallback(writer -> writer.write("ID,名前,電話番号")) // ヘッダー行
                .build();
    }

    // 4. Stepの定義 (Spring Batch 6 スタイル)
    @Bean
    public Step exportStep() {
        return new StepBuilder("exportStep", jobRepository)
                .<TUserDto, TUserDto>chunk(100) // 100件ごとにコミット/出力
                .transactionManager(transactionManager)
                .reader(reader())       // 読んで
                .processor(processor()) // 処理して
                .writer(writer())       // 書き出す
                .build();
    }

    // 5. Jobの定義
    @Bean
    public Job exportJob() {
        return new JobBuilder("exportJob", jobRepository)
                .start(exportStep())
                .build();
    }
}

とこんな感じで。

次は、DBから 1件も取得できなかったときとか、CSVに出力するレコードが
0件とか(出力する?しない?ヘッダだけ出す?出さない?)、あと例外処理か。そういうのを追加していこう。

電気ウナギ的○○

電気ウナギ的○○

About Back

コメント(0)

電気ウナギ的○○

電気ウナギ的○○

About Back

トラックバック(0)

トラックバックURL: https://blog.netandfield.com/mt/mt-tb.cgi/7242

電気ウナギ的○○

About This Site

電気ウナギ的○○
岩国在住。広島で働く超零細IT企業社長のいわゆる社長日記。
何か、酒と食い物のことばかり書いているようで・・・お察しのとおり、肥満体です:-)


2011/06/24
iPhone用サイト公開

Create with jQuery, jQTouch.


CLOSE