Spring Batchのドキュメントによると、ジョブの再起動はそのままでサポートされていますが、それを左から開始することはできません。例えば私のステップが10個のレコードを処理した場合、レコード11から開始し、再開するたびに処理が行われます。実際には、これは起こりません。初心者から読み取り、すべてを再処理します。
Java区切りファイルを読み取り、停止したところから再開できるdbテーブルにコンテンツを書き込む単純なジョブの構成ベースの構成がありますか?
@Configuration
public class BatchConfiguration {
@Value("${spring-batch.databaseType}")
private String databaseType;
@Value("${spring-batch.databaseSchema}")
private String schemaName;
@Bean
public JobBuilderFactory jobBuilderFactory(final JobRepository jobRepository) {
return new JobBuilderFactory(jobRepository);
}
@Bean
public StepBuilderFactory stepBuilderFactory(final JobRepository jobRepository,
final PlatformTransactionManager transactionManager) {
return new StepBuilderFactory(jobRepository, transactionManager);
}
@Bean
public JobRepository jobRepository(final DataSource dataSource, final PlatformTransactionManager transactionManager) {
final JobRepositoryFactoryBean bean = new JobRepositoryFactoryBean();
bean.setDatabaseType(databaseType);
bean.setDataSource(dataSource);
if (StringUtils.isNotBlank(schemaName)) {
bean.setTablePrefix(schemaName);
}
bean.setTransactionManager(transactionManager);
try {
bean.afterPropertiesSet();
return bean.getObject();
} catch (final Exception e) {
throw new BatchConfigurationException("Invalid batch job repository configuration.", e);
}
}
@Bean
public JobLauncher jobLauncher(final JobRepository jobRepository) {
final SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
}
@Configuration
@EnableScheduling
@ComponentScan("com.some.package")
public class BatchJobConfiguration {
@Resource
private JobBuilderFactory jobBuilderFactory;
@Resource
private StepBuilderFactory stepBuilderFactory;
@Value("${savings-transaction.file}")
private String savingsTransactionFile;
@Value("${savings-balance.file}")
private String savingsBalanceFile;
@Value("${processed-directory}")
private String processedDirectory;
private static final Integer IMPORT_CHUNKSIZE = 10;
@Bean
@DependsOn("stepBuilderFactory")
public Step savingsTransactionStep(final PlatformTransactionManager transactionManager,
@Qualifier("savingsTransactionItemReader") final ItemReader<SavingsTransactionItem> savingsTransactionItemReader,
@Qualifier("savingsTransactionProcessor") final ItemProcessor<SavingsTransactionItem, SavingsTransaction> processor,
@Qualifier("savingsTransactionItemWriter") final ItemWriter<SavingsTransaction> savingsTransactionItemWriter,
@Qualifier("savingsTransactionStepListener") final SavingsTransactionStepListener listener) {
return stepBuilderFactory.get("savingsTransactionStep")
.transactionManager(transactionManager)
.<SavingsTransactionItem, SavingsTransaction> chunk(IMPORT_CHUNKSIZE)
.reader(savingsTransactionItemReader)
.processor(processor)
.writer(savingsTransactionItemWriter)
.listener(listener)
.build();
}
@Bean
public Step savingsTransactionCleanUpStep(final PlatformTransactionManager transactionManager,
final JobRepository jobRepository) {
final TaskletStep taskletStep = new TaskletStep("savingsTransactionCleanUpStep");
final FileMovingTasklet tasklet = new FileMovingTasklet();
tasklet.setFileNamePattern(savingsTransactionFile);
tasklet.setProcessedDirectory(processedDirectory);
taskletStep.setTasklet(tasklet);
taskletStep.setTransactionManager(transactionManager);
taskletStep.setJobRepository(jobRepository);
try {
taskletStep.afterPropertiesSet();
} catch (final Exception e) {
throw new BatchConfigurationException("Failed to configure tasklet!", e);
}
return taskletStep;
}
@Bean
@DependsOn("jobBuilderFactory")
public Job job(final Step savingsTransactionStep,
final Step savingsTransactionCleanUpStep) {
return jobBuilderFactory.get("job")
.incrementer(new RunIdIncrementer())
.start(savingsTransactionStep)
.next(savingsTransactionCleanUpStep)
.on("FINISHED")
.end()
.build()
.build();
}
}
ジョブを再開する単体テストコード
final Date now = new Date();
jobMananger.processRegistrations(now);
final List<SavingsBalance> savingsBalances = savingsBalanceDao.findAll();
assertEquals(9, savingsBalances.size());
FileUtils.moveFile(new File("target/AEA001_20160610.dat"), new File("target/AEA001_20160610_invalid.dat"));
FileUtils.moveFile(new File("target/AEA001_20160610_valid.dat"), new File("target/AEA001_20160610.dat"));
jobMananger.processRegistrations(now);
final List<SavingsBalance> savingsBalances2 = savingsBalanceDao.findAll();
System.out.println(savingsBalances2.size());
int found = 0;
for (final SavingsBalance savingsBalance : savingsBalances2) {
final String id = savingsBalance.getId();
if ("12345".equals(id)) {
found++;
}
}
assertEquals("Invalid number of found balances!", 1, found);
ジョブマネージャーの実装
public class JobManager {
@Resource
private JobLauncher jobLauncher;
@Resource
private Job job;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void processRegistrations(final Date date) {
try {
final Map<String, JobParameter> parameters = new HashMap<>();
parameters.put("START_DATE", new JobParameter(date));
final JobParameters jobParameters = new JobParameters(parameters);
final JobExecution execution = jobLauncher.run(job, jobParameters);
LOG.info("Exit Status : " + execution.getStatus());
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
| JobParametersInvalidException e) {
LOG.error("Failed to process registrations.", e);
}
}
}
ジョブを再開できるようにするには、次のBeanを構成する必要があるようです。
@Bean
public JobOperator jobOperator(final JobLauncher jobLauncher, final JobRepository jobRepository,
final JobRegistry jobRegistry, final JobExplorer jobExplorer) {
final SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobLauncher(jobLauncher);
jobOperator.setJobRepository(jobRepository);
jobOperator.setJobRegistry(jobRegistry);
jobOperator.setJobExplorer(jobExplorer);
return jobOperator;
}
@Bean
public JobExplorer jobExplorer(final DataSource dataSource) throws Exception {
final JobExplorerFactoryBean bean = new JobExplorerFactoryBean();
bean.setDataSource(dataSource);
bean.setTablePrefix("BATCH_");
bean.setJdbcOperations(new JdbcTemplate(dataSource));
bean.afterPropertiesSet();
return bean.getObject();
}
次に、jobOperatorを使用して特定のインスタンスを再起動できるように、バッチテーブルからバッチインスタンスIDを取得する必要があります。
final Long restartId = jobOperator.restart(id);
final JobExecution restartExecution = jobExplorer.getJobExecution(restartId);
JobManagerクラス内では、JobLauncherを使用する代わりに、JobOperator.restart()nethodを使用します。
最後に失敗したステップからジョブが再開されない理由は、JobLauncherを使用してもう一度1つの新しいジョブを開始しているため、ステップ1からジョブを開始しているためです。
"restartable"プロパティがtrueに設定されていることを確認してください(デフォルトではtrueに設定されています)。
これがサンプルコードです。
public boolean resumeWorkflow(long executionId)
throws WorkflowResumeServiceException {
JobOperator jobOperator = (JobOperator) ApplicationContextProvider.getApplicationContext().getBean("jobOperator");
try
{
LOGGER.info("SUMMARY AFTER RESTART:" + jobOperator.getSummary(executionId));
jobOperator.restart(executionId);
}
}
失敗したジョブのjobExecutionidを取得して、上記のメソッドに渡す必要があります。
「FINISHED」ステータスで完了したジョブは再開できませんのでご注意ください。
この投稿も読むことができます ジョブの再開
新しいJobParametersでジョブを開始するため、SBはジョブを再開しませんが、新しいジョブを開始します。
ジョブを再開する場合は、ジョブBeanの設定からインクリメンターを削除する必要があります。