Spring批处理回滚所有块&;一次写一个文件

Spring批处理回滚所有块&;一次写一个文件,spring,spring-batch,Spring,Spring Batch,我是春季的新手,我有几个问题 问题1:我使用MultiResourceItemReader读取一堆CSV文件,使用JDBC项目编写器批量更新数据库。提交间隔设置为1000。如果有一个文件包含10k记录,并且我在第7批中遇到了DB错误,有没有办法回滚以前提交的所有数据块 问题2:如果有两个文件各有100条记录,且提交间隔设置为1000,则MultiResourceItemReader将读取这两个文件并将其发送给编写器。有没有办法一次只写一个文件而忽略提交间隔?在这种情况下,实际上是在writer中

我是春季的新手,我有几个问题

问题1:我使用MultiResourceItemReader读取一堆CSV文件,使用JDBC项目编写器批量更新数据库。提交间隔设置为1000。如果有一个文件包含10k记录,并且我在第7批中遇到了DB错误,有没有办法回滚以前提交的所有数据块


问题2:如果有两个文件各有100条记录,且提交间隔设置为1000,则MultiResourceItemReader将读取这两个文件并将其发送给编写器。有没有办法一次只写一个文件而忽略提交间隔?在这种情况下,实际上是在writer中单独创建一个循环?

问题1:实现这一点的唯一方法是通过某种形式的补偿逻辑。您可以通过监听器(例如,
ChunkListener#afterChunkError
)实现这一点,但具体实现取决于您。Spring批处理中没有任何东西知道输出的总体状态是什么,以及如何将其回滚到当前事务之外


问题2:假设您要为每个输入文件寻找一个输出文件,由于大多数
资源
实现都是非事务性的,与它们相关联的编写器会进行特殊工作,以缓冲到提交点,然后刷新。这里的问题是,正因为如此,没有真正的机会将缓冲区划分为多个资源。明确地说,这是可以做到的,你只需要一个定制的
ItemWriter
就可以做到。

发布对我有用的解决方案,以防有人需要它作为参考

对于问题1,我可以使用编写器中的
StepListenerSupport
实现它,并覆盖
BeforeStep
AfterStep
。示例代码段如下所示

public class JDBCWriter extends StepListenerSupport implements ItemWriter<MyDomain>{

private boolean errorFlag;

private String sql = "{ CALL STORED_PROC(?, ?,  ?, ?, ?) }";

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public void beforeStep(StepExecution stepExecution){

    try{
        Connection connection = jdbcTemplate.getDataSource().getConnection();

        connection.setAutoCommit(false);
    }
    catch(SQLException ex){
        setErrorFlag(Boolean.TRUE);
    }
}

@Override
public void write(List<? extends MyDomain> items) throws Exception{

    if(!items.isEmpty()){

                CallableStatement callableStatement = connection.prepareCall(sql);

                callableStatement.setString("1", "FirstName");
                callableStatement.setString("2", "LastName");
                callableStatement.setString("3", "Date of Birth");
                callableStatement.setInt("4", "Year");

                callableStatement.registerOutParameter("errors", Types.INTEGER, "");

                callableStatement.execute();

                if(errors != 0){
                    this.setErrorFlag(Boolean.TRUE);
                    }
            }
    else{
        this.setErrorFlag(Boolean.TRUE);
    }
}

@Override
public void afterChunk(ChunkContext context){
    if(errorFlag){
        context.getStepContext().getStepExecution().setExitStatus(ExitStatus.FAILED); //Fail the Step
        context.getStepContext().getStepExecution().setStatus(BatchStatus.FAILED); //Fail the batch
    }
}

@Override
public ExitStatus afterStep(StepExecution stepExecution){
    try{
        if(!errorFlag){
            connection.commit();
        }
        else{
            connection.rollback();
            stepExecution.setExitStatus(ExitStatus.FAILED);
        }
    }
    catch(SQLException ex){
        LOG.error("Commit Failed!" + ex);
    }

    return stepExecution.getExitStatus();
}

public void setErrorFlag(boolean errorFlag){
    this.errorFlag = errorFlag;
    }
}
公共类JDBCWriter扩展StepListenerSupport实现ItemWriter{
私有布尔错误标志;
私有字符串sql=“{CALL STORED_PROC(?,,,,?,?)}”;
@自动连线
私有JdbcTemplate JdbcTemplate;
@凌驾
预处理前的公共无效(步骤执行步骤执行){
试一试{
连接连接=jdbcTemplate.getDataSource().getConnection();
connection.setAutoCommit(false);
}
catch(SQLException-ex){
setErrorFlag(Boolean.TRUE);
}
}
@凌驾

公开无效写入(ListThank!关于问题2,我正在考虑使用某种类型的
PeakableItemReader
实现自定义的
CompletionPolicy
,但它运行在一个无限循环中。是否有关于如何将
MultiResourceItemReader
PeakableItemReader
连接起来的工作示例?对于问题2,您不需要要修复读卡器中的内容,您需要修复编写器中的内容。您将已经知道资源已更改(假设您正在对项目使用
MultiResourceItemReader
ResourceAware
)。您只需要在写入时为每个资源创建一个新的缓冲区,并在提交点将其刷新到相应的资源。正如您所建议的,我正在对该项使用
MultiResourceItemReader
ResourceAware
,关于每个资源的缓冲区,您能否详细说明,并在可能的情况下为我指出任何示例?谢谢!我愿意没有示例。但是我可以想象一个
映射,在该映射中,您根据通过的资源写入相应的
StringBuilder
。然后,当提交发生时,将每个
StringBuilder
刷新到正确的资源。通过查看
FlatFileItemWr,您可以看到缓冲区处理和刷新是如何工作的国际热核实验堆
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    ....
    http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">

<job id="fileLoadJob" xmlns="http://www.springframework.org/schema/batch">

    <step id="batchFileUpload" >
        <tasklet>
            <chunk reader="fileReader"
                   commit-interval="1000"
                   writer="JDBCWriter"
            />
        </tasklet>
    </step>

</job>

<bean id="fileReader" class="...com.FileReader" />
<bean id="JDBCWriter" class="...com.JDBCWriter" />

</beans>