When I use Spring Batch
with @EnableScheduling
and @Scheduled(fixedRate = 60000, zone = “Asia/Shanghai”)
, after starting the project I get [ main]
and [ scheduling-1]
executing simultaneously. But at the same time the [ main]
execution does not access the following code but simply executes the exportStep
, which results in empty JobParameters
.
@Scheduled(fixedRate = 60000, zone = "Asia/Shanghai")
public void runBatchJob()
I'd like to be able to leave [ main]
unexecuted at startup, or also call the corresponding method to get JobParameters
when it executes, I'd prefer to be able to achieve the first effect because [ scheduling-1]
can replace him and not have to perform the same operation twice at startup.
----- Supplement Content -----
Clarify the role of the [main] thread in your Spring Batch configuration, as it seems to be running tasks unintentionally at startup.
For my feature, I don't want the [main]
thread to do anything, the things to be done should be left to the [scheduling-1]
thread, but the [main]
thread performs what the [scheduling-1]
thread is supposed to do all over again on startup (and that's not done completely)
whether you want to prevent the main thread from triggering the scheduled task at startup or ensure that the scheduled task executes only once.
I want to prevent the main thread from triggering the scheduled task at startup
A clear description of the job flow and any relevant Spring Batch configuration would help refine the solution.
Here's the code I think is important
@Configuration
@EnableBatchProcessing
public class BatchConfig {
private final DataSource dataSource;
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
public BatchConfig(DataSource dataSource, JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory) {
this.dataSource = dataSource;
this.jobBuilderFactory = jobBuilderFactory;
this.stepBuilderFactory = stepBuilderFactory;
}
@Bean
public Job transactionExportJob(Step exportStep, BatchJobCompletionListener listener) {
return jobBuilderFactory.get("transactionExportJob")
.incrementer(new RunIdIncrementer())
.start(exportStep)
.listener(listener)
.build();
}
@Bean
public Step exportStep(ItemReader<Transaction> reader, ExcelWriter excelWriter, PlatformTransactionManager transactionManager) {
return stepBuilderFactory.get("exportStep")
.<Transaction, Transaction>chunk(5000)
.reader(reader)
.processor(new TransactionProcessor()) // handle data
.writer(excelWriter)
.faultTolerant() // enabling fault tolerance
.retryLimit(3)
.retry(Exception.class)
.skip(Exception.class)
.skipLimit(100)
.listener(new CustomSkipListener()) // listing skip
.transactionManager(transactionManager)
.build();
}
@Bean
public JdbcPagingItemReader<Transaction> reader(PagingQueryProvider transactionQueryProvider) {
JdbcPagingItemReader<Transaction> reader = new JdbcPagingItemReader<>();
reader.setDataSource(dataSource);
reader.setFetchSize(5000);
reader.setRowMapper(new TransactionRowMapper());
reader.setQueryProvider(transactionQueryProvider);
return reader;
}
}
@Component
public class ExcelWriter implements ItemWriter<Transaction> {
private final String filePath;
private final Workbook workbook;
private final Sheet sheet;
private int rowCount = 1;
public ExcelWriter() {
this.filePath = "transactions_" + UUID.randomUUID().toString() + ".xlsx";
this.workbook = new XSSFWorkbook();
this.sheet = workbook.createSheet("Transactions");
createHeaderRow();
}
@Override
public void write(List<? extends Transaction> items) throws Exception {
System.out.println("filePath: " + filePath);
for (Transaction transaction : items) {
Row row = sheet.createRow(rowCount++);
row.createCell(0).setCellValue(transaction.getUniqueTxId());
row.createCell(1).setCellValue(transaction.getCreatedTime().toString());
}
try (FileOutputStream out = new FileOutputStream(filePath)) {
workbook.write(out);
} catch (IOException e) {
System.err.println("Error closing Excel file: " + e.getMessage());
throw new IOException("Failed to close workbook", e);
}
}
private void createHeaderRow() {
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("Unique Tx Id");
header.createCell(1).setCellValue("Created Time");
}
public void closeWorkbook() throws IOException {
workbook.close();
}
}
@Component
@EnableScheduling
public class BatchScheduler {
private final JobLauncher jobLauncher;
private final Job transactionExportJob;
private final JobParametersHolder jobParametersHolder;
public BatchScheduler(JobLauncher jobLauncher, Job transactionExportJob, JobParametersHolder jobParametersHolder) {
this.jobLauncher = jobLauncher;
this.transactionExportJob = transactionExportJob;
this.jobParametersHolder = jobParametersHolder;
}
@Scheduled(fixedRate = 60000, initialDelay = 60000, zone = "Asia/Shanghai")
public void runBatchJob() {
try {
ExcelWriter excelWriter = new ExcelWriter();
LocalDateTime startTime = LocalDateTime.now().minusDays(1).withHour(22);
LocalDateTime endTime = LocalDateTime.now().withHour(22);
JobParameters jobParameters = new JobParametersBuilder()
.addDate("startTime", Date.from(startTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant()))
.addDate("endTime", Date.from(endTime.atZone(ZoneId.of("Asia/Shanghai")).toInstant()))
.toJobParameters();
System.out.println("jobParameters: " + jobParameters);
jobParametersHolder.setJobParameters(jobParameters);
System.out.println("Batch Job started");
JobExecution execution = jobLauncher.run(transactionExportJob, jobParameters);
System.out.println("Batch job completed with status: {}" + execution.getStatus());
excelWriter.closeWorkbook();
} catch (Exception e) {
System.out.println("Batch job execution failed: {}" + e.getMessage() + e);
}
}
}
Thanks a lot @rahulP, the default parameter true
for spring.batch.job.enabled
caused the Batch task to start executing as soon as the application was started, and I wanted the opposite, so I changed the default parameter to false
and it worked.