Java Spring批处理:使用自定义批处理大小将列表写入数据库表

Java Spring批处理:使用自定义批处理大小将列表写入数据库表,java,spring-batch,spring-jdbc,jdbctemplate,itemwriter,Java,Spring Batch,Spring Jdbc,Jdbctemplate,Itemwriter,背景 我有一份Spring批处理工作,其中: FlatFileItemReader-从文件中一次读取一行 ItemProcesor-将文件中的行转换为列表,并返回列表。也就是说,文件中的每一行都被分解为一个列表(文件中的一行转换为多个输出行) ItemWriter-将列表写入数据库表。(我用过 解包从处理器接收的列表的实现 并授权给JdbcBatchItemWriter) 问题 在点2),处理器可以返回100000个MyObject实例的列表 在第3点),委托JdbcBatchItemWrit

背景

我有一份Spring批处理工作,其中:

  • FlatFileItemReader
    -从文件中一次读取一行
  • ItemProcesor
    -将文件中的行转换为
    列表
    ,并返回
    列表
    。也就是说,文件中的每一行都被分解为一个
    列表
    (文件中的一行转换为多个输出行)
  • ItemWriter
    -将
    列表
    写入数据库表。(我用过 解包从处理器接收的列表的实现 并授权给
    JdbcBatchItemWriter
  • 问题

    • 在点2),处理器可以返回100000个
      MyObject
      实例的
      列表
    • 在第3点),委托
      JdbcBatchItemWriter
      将最终将包含100000个对象的整个
      列表写入数据库
      

    我的问题是:
    JdbcBatchItemWriter
    不允许自定义批量大小。出于所有实际目的,该步骤的批处理大小=提交间隔。考虑到这一点,Spring批处理中是否有另一个
    ItemWriter
    实现,它允许写入数据库并允许可配置的批处理大小?如果没有,如何着手自己编写自定义编写器来实现这一点?

    我看不到明显的方法来设置
    JdbcBatchItemWriter
    上的批大小。但是,您可以扩展编写器并使用自定义的
    BatchPreparedStatementSetter
    指定批大小。下面是一个简单的例子:

    public class MyCustomWriter<T> extends JdbcBatchItemWriter<T> {
    
        @Override
        public void write(List<? extends T> items) throws Exception {
            namedParameterJdbcTemplate.getJdbcOperations().batchUpdate("your sql", new BatchPreparedStatementSetter() {
                @Override
                public void setValues(PreparedStatement ps, int i) throws SQLException {
                    // set values on your sql
                }
    
                @Override
                public int getBatchSize() {
                    return items.size(); // or any other value you want
                }
            });
        }
    
    }
    
    公共类MyCustomWriter扩展了JdbcBatchItemWriter{
    @凌驾
    public void write(List来自的答案和评论几乎涵盖了解决方案的所有方面,是公认的答案

    如果有人感兴趣,下面是我使用的实现:

    public class JdbcCustomBatchSizeItemWriter<W> extends JdbcDaoSupport implements ItemWriter<W> {
    
        private int batchSize;
        private ParameterizedPreparedStatementSetter<W> preparedStatementSetter;
        private String sqlFileLocation;
        private String sql;
    
        public void initReader() {
            this.setSql(FileUtilties.getFileContent(sqlFileLocation));
        }
    
        public void write(List<? extends W> arg0) throws Exception {
            getJdbcTemplate().batchUpdate(sql, Collections.unmodifiableList(arg0), batchSize, preparedStatementSetter);
        }
    
        public void setBatchSize(int batchSize) {
            this.batchSize = batchSize;
        }
    
        public void setPreparedStatementSetter(ParameterizedPreparedStatementSetter<W> preparedStatementSetter) {
            this.preparedStatementSetter = preparedStatementSetter;
        }
    
        public void setSqlFileLocation(String sqlFileLocation) {
            this.sqlFileLocation = sqlFileLocation;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    }
    
    公共类JdbcCustomBatchSizeItemWriter扩展JDBCDAO支持实现ItemWriter{
    私有整数批量大小;
    私有参数化preparedStatementSetter preparedStatementSetter;
    私有字符串sqlFileLocation;
    私有字符串sql;
    public void initReader(){
    this.setSql(fileuties.getFileContent(sqlFileLocation));
    }
    
    公开无效写入(列表我不会这样做。它会带来可重启性问题。相反,请修改您的读卡器以生成单个项,而不是让处理器接收对象并返回列表。

    @MohmoudBenHassine这看起来很有希望。关于
    StagingItemWriter
    示例,请不要使用
    BatchPreparedStatementSetter
    ,我可以简单地调用
    write
    方法,将所需的批处理大小传递给它?是的,这是另一个选项。我已经得到了前进的必要方向。谢谢。很高兴它有帮助!但是,这很棘手。无论处理逻辑产生多少项,整个数据块都将有一个事务。例如,如果提交大小=10,且处理器为每个项目返回100个项目,则写入程序将在每个区块中接收1000个项目。即使jdbc batch_size=500,这些1000个项目也将在单个事务中提交。批次大小控制将向db发送的查询数量(在本例中为2个batchUpdate查询,每个批次500个项目)而不是多少个事务(在本例中是由Spring Batch驱动的单个事务)。没有问题。区块处理的思想是将整个区块作为一个单元进行处理(全部或全无语义)。只是想澄清您尝试自定义的批次大小不影响此模型(它只影响将向数据库发送多少查询)。将文件中的一行转换为多行所需的处理相对复杂,无法放入
    ItemReader
    。如果考虑到我的作业永远不需要重新启动,我不需要重新启动作业,这种方法是否还有其他问题?