出力項目を増やすようちょっと修正版 Spring Batch

色々と Spring Batch 6 で試している元のソースは「一番シンプルな Spring Batch 6 の Chunk 処理例」に掲載しているものだと言っているんだが、実は変更されている。

真面目に「一番シンプルな Spring Batch 6 の Chunk 処理例」のソースを使って、その後の記事の変更点を適用しようとしたらなんかコードが違う!?と戸惑われている方がいらっしゃるかもしれないので、一応、変更後のソースも載せておこう。

一番シンプルな Spring Batch 6 の Chunk 処理例」のソースを参照・・・と書かれていたら、実際にはこのソースを使用する。

テーブルも複数参照するように変わっているが JOIN で連結しているので SQL 文は一本だ。追加したテーブルは以下のとおり。

t_user_addresses(ユーザ住所)

20260713_base_springbatch1.jpg

t_user_anniversaries(ユーザ記念日)

20260713_base_springbatch2.jpg

これを、以前作った t_users(ユーザ情報テーブル)と連結して CSV に出力するように修正した。

20260713_base_springbatch3.jpg

ただ、 SQL が変わったのと、出力する項目数が増えただけなので、基本的な構造は変わらない。

以下、修正点。


■Dto/UserCombinedDto.java(record クラス)

package com.netandfield.test.Dto;

public record UserCombinedDto(
    Long user_id,
    String user_name,
    String user_address1,
    String user_address2,
    String user_phone_number,
    String anniversary,
    String note
) {}

※以前作った TUserDto.java は廃止。


■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.UserCombinedDto;

@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<UserCombinedDto> reader() {

        // 記念日が複数ある場合、SQLのJOINによって自動的にユーザー1人あたり複数行(複数レコード)として取得されます
        String joinSql = """
            SELECT
                u.user_id,
                u.user_name,
                a.user_address1,
                a.user_address2,
                u.user_phone_number,
                an.anniversary,
                an.note
            FROM t_users u
            LEFT JOIN t_user_addresses a ON u.user_id = a.user_id
            LEFT JOIN t_user_anniversaries an ON u.user_id = an.user_id
            ORDER BY u.user_id ASC, an.anniversary ASC
            """;
        return new JdbcCursorItemReaderBuilder<UserCombinedDto>()
                .dataSource(dataSource)
                .name("oracleItemReader")
                .sql(joinSql)
                .rowMapper(new DataClassRowMapper<>(UserCombinedDto.class)) // RowMapperでResultSet(検索結果オブジェクト)をDTOへマッピング
                .fetchSize(1000) // Oracleのパフォーマンス最適化(1,000行ずつ読込)
                .build();
    }

    // 2. Processor: データを加工する
    @Bean
    @StepScope // ジョブ引数を受け取るために必須
    public ItemProcessor<UserCombinedDto, UserCombinedDto> 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 UserCombinedDto(
                        item.user_id(),
                        item.user_name(),
                        item.user_address1(),
                        item.user_address2(),
                        phone + "(携帯)",
                        item.anniversary(),
                        item.note()
                );            }

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

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

    // 4. Stepの定義 (Spring Batch 6 スタイル)
    @Bean
    public Step exportStep () {
        return new StepBuilder("exportStep", jobRepository)
                .<UserCombinedDto, UserCombinedDto>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())
                .listener(jobListener) // JobExecutionListenerを追加
                .build();
    }
}


これで実行すれば、

20260713_base_springbatch4.jpg

こういう CSV ファイルが出力される。

トラックバック(0)

このブログ記事を参照しているブログ一覧: 出力項目を増やすようちょっと修正版 Spring Batch

このブログ記事に対するトラックバックURL: https://blog.netandfield.com/mt/mt-tb.cgi/7244

コメントする

このブログ記事について

このページは、shinodaが2026年7月13日 21:47に書いたブログ記事です。

ひとつ前のブログ記事は「「あんてい -はなれ-」のラーメンには肥満児も満足」です。

次のブログ記事は「「ロッキー」は立ち呑み屋じゃないのでお通し代あるよって話」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら