Spring 批处理步骤未立即提交事务

Spring Batch step not committing transaction immediately

我配置了读取和验证数据的步骤。如果任何验证失败,我将错误 [​​=18=](创建以跟踪文件详细信息)状态更新为失败,并将原因更新为验证消息(BatchFileDetail 在下面的代码中为错误 table)。 一旦将其保存在数据库中,我立即根据错误 table 状态

抛出异常

下面是我的代码

    @Bean
    public Job fileParserJob() {
        return jobBuilderFactory.get("fileParserJob")
                .incrementer(new RunIdIncrementer())
                .start(validateFileStep())
                .build();
    }

    public Step validateFileStep() {
        return stepBuilderFactory.get("validateFileStep")
                .tasklet(fileTasklet)
                .build();
    }

    @Override
    @Transactional(dontRollbackOn=BatchServiceException.class)
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws 
    Exception {
        Resource resource = new FileSystemResource(filePath);
        BatchFileDetail batchFileDetail = new BatchFileDetail();
        batchFileDetail.setFileId(String.valueOf(System.currentTimeMillis()));
        batchFileDetail.setFileName(resource.getFilename());
        batchFileDetail.setStatus("STARTED");
        batchFileDetail.setProcessedOn(new Date());
        CommonUtillity.validateBatchResource(resource, StringConstants.FILE_NAME, batchFileDetail);
        validateBatchFileDetail(resource.getFile(), batchFileDetail);
        batchFileDetailRepository.saveAndFlush(batchFileDetail);
        if(CBOSConstants.FAILED.equals(batchFileDetail.getStatus())) {
            throw new BatchServiceException(batchFileDetail.getReason());
        }
        return RepeatStatus.FINISHED;
    }

}

但是我在错误 table 中保存的数据正在回滚。处理异常会导致继续执行另一个步骤,但我想终止当前作业并输入错误 table。 任何帮助表示赞赏。

您正在使用 @Transactional(dontRollbackOn=BatchServiceException.class) 注释您的 tasklet。这不是告诉 Spring Batch 不回滚给定异常类型的事务的方法。

您的 tasklet 将在由 Spring 批处理控制的事务范围内执行,您可以通过事务属性自定义批处理,请参阅 AbstractTaskletStepBuilder#transactionAttribute

所以在你的情况下,你应该能够通过删除 tasklet 上的 @Transactional 来实现你的要求,并用类似的东西定义你的步骤:

@Bean
public Step validateFileStep() {
    TransactionAttribute transactionAttributes = new DefaultTransactionAttribute() {
        @Override
        public boolean rollbackOn(Throwable exception) {
            return !(exception instanceof BatchServiceException);
        }
    };

    return stepBuilderFactory.get("validateFileStep")
            .tasklet(fileTasklet)
            .transactionAttribute(transactionAttributes)
            .build();
}

这是一个简单的 tasklet。对于面向块的 tasklet,可以使用 FaultTolerantStepBuilder#noRollback(Class).

我找到的解决方案可能不是最好的,但根据目前的情况,我会继续进行以下操作。

@Bean
public Job fileParserJob() {
    return jobBuilderFactory.get("fileParserJob")
            .incrementer(new RunIdIncrementer())
            .start(validateFileStep())
            .next(validateFileStatusStep())
            .build();
}

public Step validateFileStep() {
    return stepBuilderFactory.get("validateFileStep")
            .tasklet(fileTasklet)
            .build();
}

public Step validateFileStatusStep() {
    return stepBuilderFactory.get("validateFileStatusStep")
            .tasklet(validateBatchFileDetailTasklet)
            .build();
}

FileTasklet.java
-----------------
@Override
@Transactional(dontRollbackOn=BatchServiceException.class)
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
    Resource resource = new FileSystemResource(filePath);
    BatchFileDetail batchFileDetail = new BatchFileDetail();
    batchFileDetail.setFileId(String.valueOf(System.currentTimeMillis()));
    batchFileDetail.setFileName(resource.getFilename());
    batchFileDetail.setStatus("STARTED");
    batchFileDetail.setProcessedOn(new Date());
    CommonUtillity.validateBatchResource(resource, StringConstants.FILE_NAME, batchFileDetail);
    validateBatchFileDetail(resource.getFile(), batchFileDetail);
    batchFileDetailRepository.saveAndFlush(batchFileDetail);
    return RepeatStatus.FINISHED;
}

}

ValidateBatchFileDetailTasklet
------------------------------
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
    BatchFileDetail batchFileDetail = batchFileDetailRepository.getRecentFailedTransactionStatus(StringConstants.FILE_NAME);
    if(StringConstants.FAILED.equals(batchFileDetail.getStatus())) {
        throw new BatchServiceException(batchFileDetail.getReason());
    }
    return RepeatStatus.FINISHED;
}

使用此解决方案,第一个错误 table 更新失败状态和失败原因,然后一步提交事务。在下一步中,根据文件名获取最近的记录并检查获取的记录的状态。如果是 FAILED,则抛出异常以终止当前作业。

如果状态不仅仅是FAILED,则必须处理下一个数据。如果状态为 FAILED,我们必须终止当前批处理作业。