色々と Spring Batch 6 で試している元のソースは「一番シンプルな Spring Batch 6 の Chunk 処理例」に掲載しているものだと言っているんだが、実は変更されている。
真面目に「一番シンプルな Spring Batch 6 の Chunk 処理例」のソースを使って、その後の記事の変更点を適用しようとしたらなんかコードが違う!?と戸惑われている方がいらっしゃるかもしれないので、一応、変更後のソースも載せておこう。
「一番シンプルな Spring Batch 6 の Chunk 処理例」のソースを参照・・・と書かれていたら、実際にはこのソースを使用する。
テーブルも複数参照するように変わっているが JOIN で連結しているので SQL 文は一本だ。追加したテーブルは以下のとおり。
t_user_addresses(ユーザ住所)
t_user_anniversaries(ユーザ記念日)
これを、以前作った t_users(ユーザ情報テーブル)と連結して CSV に出力するように修正した。
ただ、 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;@Configurationpublic 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からデータを読み込む@Beanpublic JdbcCursorItemReader<UserCombinedDto> reader() {// 記念日が複数ある場合、SQLのJOINによって自動的にユーザー1人あたり複数行(複数レコード)として取得されますString joinSql = """SELECTu.user_id,u.user_name,a.user_address1,a.user_address2,u.user_phone_number,an.anniversary,an.noteFROM t_users uLEFT JOIN t_user_addresses a ON u.user_id = a.user_idLEFT JOIN t_user_anniversaries an ON u.user_id = an.user_idORDER 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ファイルへ出力する@Beanpublic 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 スタイル)@Beanpublic Step exportStep () {return new StepBuilder("exportStep", jobRepository).<UserCombinedDto, UserCombinedDto>chunk(100) // 100件ごとにコミット/出力.transactionManager(transactionManager).reader(reader()) // 読んで.processor(processor()) // 処理して.writer(writer()) // 書き出す.build();}// 5. Jobの定義@Beanpublic Job exportJob() {return new JobBuilder("exportJob", jobRepository).start(exportStep()).listener(jobListener) // JobExecutionListenerを追加.build();}}
これで実行すれば、
こういう CSV ファイルが出力される。

コメントする