tohnamanブログ

東村山に生息するWindows好きな太っちょオヤジプログラマのブログ

Spring Batch その8

今回もSpring Batch です。


今回は、成功後の再実施で一意制約違反エラーで失敗する問題に対応します。

解決方法はいろいろありますが、作成しているバッチをマスタ更新バッチと位置付けて、
以下の仕様に変更します。

  1. 所定の場所にファイルが存在する場合にマスタ更新を実行する
  2. マスタ更新処理は、デリート&インサートで行う

では、早速行ってみよう!

マスタ更新ファイルの存在チェック

Tasklet作成
public class FileExistCheckTasklet implements Tasklet, InitializingBean {

  /** ファイルが存在しない場合のExitStatus */
  public static final String NOT_EXIST = "NOT_EXIST";

  /** ファイルパス */
  private String filePath;

  @Override
  public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
    if (!Files.exists(Paths.get(filePath))) {
      contribution.setExitStatus(new ExitStatus(NOT_EXIST));
    }
    return RepeatStatus.FINISHED;
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    Assert.notNull(filePath, "ファイルパスは必須です。");
  }

  public void setFilePath(String filePath) {
    this.filePath = filePath;
  }
}

汎用的に、ファイル存在チェックTaskletとして作成

マスタ更新ファイルの存在確認Step
  @Bean
  public Step fileExistCheckStep() {
    // @formatter:off
    return stepBuilderFactory
        .get("fileExistCheckStep")
        .tasklet(fileExistCheckTasklet())
        .build();
    // @formatter:on
  }

  @Value("${csv2dbjob.filepath}")
  private String filePath;

  @Bean
  public FileExistCheckTasklet fileExistCheckTasklet() {
    FileExistCheckTasklet tasklet = new FileExistCheckTasklet();
    tasklet.setFilePath(filePath);
    return tasklet;
  }

作成したTaskletを使用するStepを作成
Taskletの定義では、ファイルパスを設定ファイルから取得するようにしています

  # csv2dbjob
  csv2dbjob.filepath=D:/work/MastarUpdate.txt

設定ファイルはこんなかんじ

マスタ更新処理の修正

マスタ削除Tasklet作成
public class MasterDeleteTasklet implements Tasklet {
  @Autowired
  private MstAddressMapper mstAddressMapper;

  @Override
  public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
    this.mstAddressMapper.deleteByExample(null);
    return RepeatStatus.FINISHED;
  }
}

WHERE句に何も設定しないDETETE文を実行するだけの単純なTasklet

マスタ削除Step
@Autowired
private MasterDeleteTasklet masterDeleteTasklet;

@Bean
public Step masterDeleteStep() {
  // @formatter:off
  return stepBuilderFactory
      .get("masterDeleteStep")
      .tasklet(masterDeleteTasklet)
      .build();
  // @formatter:on
}

@Bean
public MasterDeleteTasklet masterDeleteTasklet() {
  return new MasterDeleteTasklet();
}

作成したマスタ削除Taskletを使用するStepを作成
ここでは定義したTaskletを DI しています。
理由は、MasterDeleteTasklet でmapperをDIしている為で、
定義したBeanをそのまま利用するとマスタ削除時に、
mapperがDIされず、ぬるぽが発生します。

Jobの修正

修正前
  public Job csv2dbJob() {
    // @formatter:off
    return jobBuilderFactory
        .get("csv2dbJob")
        .listener(logListener)
        .flow(csv2dbStep())
        .end()
        .build();
    // @formatter:on
  }
修正後
@Bean
public Job csv2dbJob() {
  // @formatter:off
  return jobBuilderFactory
      .get("csv2dbJob")
      .listener(logListener)
      .start(fileExistCheckStep())
        .on(FileExistCheckTasklet.NOT_EXIST).end()
      .from(fileExistCheckStep())
        .next(masterDeleteStep())
        .next(csv2dbStep())
        .build()
      .build();
  // @formatter:on
}

SpringBatchの条件分岐を利用したJobに変更しました。
ファイルが存在しない場合に設定したExitStatusをチェックし、
NOT_EXISTの場合はJobを終了します。
この時、masterDeleteStep、csv2dbStepは実行されません。
フローはこんな感じです
f:id:tohnaman:20170218145034p:plain

これで、その6で上げた不満点は、全て解消されました。

完成したモノをGitHubここ に置いています。

現在、私の中でSpringBatchよりもWindowsのUWPアプリ開発の優先度が高いので、
気分転換に書くかもですが、SpringBatchは一旦、ここまでとします。