Spring ClassifierCompositeItemWriter在异常时回滚,但数据部分提交到数据库

Spring ClassifierCompositeItemWriter在异常时回滚,但数据部分提交到数据库,spring,spring-boot,transactions,spring-batch,spring-jdbc,Spring,Spring Boot,Transactions,Spring Batch,Spring Jdbc,我使用ClassifierCompositeItemWriter在一个固定长度的平面文件中插入不同类型的寄存器,并将其写入 postgres数据库具有多个JDBCBatchItemWriter,每个JDBCBatchItemWriter位于不同的表中,所有这些都在一个步骤中,然后坚持一个spring批处理作业,它工作正常,但在激活事务时,它们不会在异常情况下回滚 例如,我有一个32行的平面文件,1行是页眉记录,然后我将其插入页眉表中,然后有30条常规记录和1条页脚记录(按顺序),然后在常规记录的

我使用ClassifierCompositeItemWriter在一个固定长度的平面文件中插入不同类型的寄存器,并将其写入 postgres数据库具有多个JDBCBatchItemWriter,每个JDBCBatchItemWriter位于不同的表中,所有这些都在一个步骤中,然后坚持一个spring批处理作业,它工作正常,但在激活事务时,它们不会在异常情况下回滚

例如,我有一个32行的平面文件,1行是页眉记录,然后我将其插入页眉表中,然后有30条常规记录和1条页脚记录(按顺序),然后在常规记录的记录29中,由于数据库转换异常而失败(为测试创建的错误)然后它以作业的失败状态结束,这是正常的,但是当我查看数据库时,我发现1条记录为页眉,29条记录为常规数据(有错误的除外),没有页脚记录,但我希望事务回滚1条记录为页眉,其余29条记录,但在异常回滚之后,它们仍然在数据库中

我不知道我是否错了,spring批处理中的事务是否以这种方式工作,或者这是我的配置中的错误还是什么

以下是ClassifierCompositeItemWriter和一个ItemWriter的代码,其他类似于此:

public ClassifierCompositeItemWriter<DTOBase> altasOffWriterClassifier(DataSource dataSource) {

    BackToBackPatternClassifier classifier = new BackToBackPatternClassifier();
    classifier.setRouterDelegate(dtoWriterClassifier);
    classifier.setMatcherMap(new HashMap<String, JdbcBatchItemWriter<? extends DTOBase>>() {
        private static final long serialVersionUID = -1247536568421993759L;
    {
        put(DTOHeader.class.getTypeName(), headerWriter());
        put(DTOData.class.getTypeName(), dataWriter());
        put(DTOFooter.class.getTypeName(), footerWriterFin());
    }});

    ClassifierCompositeItemWriter<DTOBase> writer = new ClassifierCompositeItemWriter<>();
    writer.setClassifier(classifier);

    return writer;
}

@Bean
public JdbcBatchItemWriter<DTOAltaOFF> altaOffWriter() {
    return new JdbcBatchItemWriterBuilder<DTOData>()
         .dataSource(dataSource)
         .sql("insert into tabla(ticket, identificador, fecha_alta_operacion, "
                + " ordenante, numero, moneda, cif, importe_emisor, "
                + " estado, telefono_destino, fecha_caducidad_hal, concepto, cabecera_num_orden_fichero) "
                + " VALUES (:ticket,:identificador,to_timestamp(:fechaAltaOperacion,'DDMMYYYYHH24MISS'), "
                + " :ordenante,:numero,:moneda,:cif,(cast(:importeEmisor as double precision)/100), "
                + " :estado,:telefonoDestino,to_timestamp(:fechaCaducidadHal,'DDMMYYYYHH24MISS'),:concepto,:idCabecera) ")
         .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>()) 
         .build();     
}
数据源

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

...some @Value...   
    @Bean(name = "dataSource")
    public DriverManagerDataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(datasourceUrl);
        dataSource.setUsername(usuario);
        dataSource.setPassword(clave);
        return dataSource;
    }
}
配置:

@Configuration
@EnableBatchProcessing
@Import({ DataSourceConfig.class })
@PropertySource("classpath:batch.properties")
@ComponentScan({ "..."})
public class BatchConfiguration {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Autowired
    public JobRepository jobRepository;

    @Autowired
    public DataSource dataSource;

    @Bean
    public JdbcTemplate getJdbcTemplate() {
        return new JdbcTemplate(dataSource);
    }

    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}
自定义:

@Component
@EnableTransactionManagement
public class CustomBatchConfigurer extends DefaultBatchConfigurer {

   private final TaskExecutor taskExecutor;

    public CustomBatchConfigurer(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    @Override
    protected JobLauncher createJobLauncher() throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(getJobRepository());
        jobLauncher.setTaskExecutor(this.taskExecutor);
        jobLauncher.afterPropertiesSet();
        return jobLauncher;
    }

    @Autowired
    private DataSource dataSource;

    @Override
    public PlatformTransactionManager getTransactionManager() {
        DataSourceTransactionManager tm = new DataSourceTransactionManager();
        tm.setDataSource(dataSource);
        return tm;
    }
}

任何帮助都会很好。

正如@MahmoudBenHassine在评论中写道的,这是块大小,我是这样添加的

@Bean
public Step Step1(@Qualifier("xyzWriterClassifier") ItemWriter<DTOxyz> writer) throws Exception {
    return stepBuilderFactory.get("Step1")
            .<DTOxyz, DTOxyz> chunk(100)
            .reader(dtoXyzItemReader(NULL because WILL_BE_INJECTED))
            .processor(XyzProcessor())
            .writer(writer)
            .build();        
}
@Bean
公共步骤Step1(@Qualifier(“xyzWriterClassifier”)ItemWriter)引发异常{
返回stepBuilderFactory.get(“Step1”)
.chunk(100)
.reader(dtoXyzItemReader(空,因为将被注入))
.processor(XyzProcessor())
.作者(作者)
.build();
}

正如@MahmoudBenHassine在评论中写到的,这是块大小,我是这样添加的

@Bean
public Step Step1(@Qualifier("xyzWriterClassifier") ItemWriter<DTOxyz> writer) throws Exception {
    return stepBuilderFactory.get("Step1")
            .<DTOxyz, DTOxyz> chunk(100)
            .reader(dtoXyzItemReader(NULL because WILL_BE_INJECTED))
            .processor(XyzProcessor())
            .writer(writer)
            .build();        
}
@Bean
公共步骤Step1(@Qualifier(“xyzWriterClassifier”)ItemWriter)引发异常{
返回stepBuilderFactory.get(“Step1”)
.chunk(100)
.reader(dtoXyzItemReader(空,因为将被注入))
.processor(XyzProcessor())
.作者(作者)
.build();
}

您的区块大小是多少?如果是,我尝试将写入程序注册为流,但出现了一个错误:AbstractTaskletStepBuilder类型中的方法流(ItemStream)不适用于参数(JdbcBatchItemWriter)*,我在一本书中发现:*JdbcBatchItemWriter没有实现ItemStream接口,因为它不维护任何状态。关于事务,我很困惑,我不知道我是否能够在异常情况下回滚所有项目,这是另一回事。此功能称为区块扫描,当写入程序抛出可跳过的异常时触发。SpringBatch将逐项“扫描”区块,并为每个项目提交一个事务(从技术上讲,它将动态地将区块大小更改为1)。所以,如果你有这个,这意味着你已经配置了一个可跳过的异常类型,并且该类型已经从你的编写器中抛出。不,我没有使用SkipPolicy,我的问题是我需要回滚数据库事务中插入的所有数据,以防抛出异常,现在的行为是:job stop and get failed status for step and job,但此时JdbcBatchItemWriter插入的所有数据继续提交到各自的表中,就像没有应用回滚一样。
不,我没有使用SkipPolicy
,在这种情况下,块大小应该已经等于1,如我在第一条注释中所述。否则,请共享您的步骤配置或提供复制问题的方法。您的区块大小是多少?如果是,我尝试将写入程序注册为流,但出现了一个错误:AbstractTaskletStepBuilder类型中的方法流(ItemStream)不适用于参数(JdbcBatchItemWriter)*,我在一本书中发现:*JdbcBatchItemWriter没有实现ItemStream接口,因为它不维护任何状态。关于事务,我很困惑,我不知道我是否能够在异常情况下回滚所有项目,这是另一回事。此功能称为区块扫描,当写入程序抛出可跳过的异常时触发。SpringBatch将逐项“扫描”区块,并为每个项目提交一个事务(从技术上讲,它将动态地将区块大小更改为1)。所以,如果你有这个,这意味着你已经配置了一个可跳过的异常类型,并且该类型已经从你的编写器中抛出。不,我没有使用SkipPolicy,我的问题是我需要回滚数据库事务中插入的所有数据,以防抛出异常,现在的行为是:job stop and get failed status for step and job,但此时JdbcBatchItemWriter插入的所有数据继续提交到各自的表中,就像没有应用回滚一样。
不,我没有使用SkipPolicy
,在这种情况下,块大小应该已经等于1,如我在第一条注释中所述。否则,请共享您的step配置或提供复制此问题的。