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

Spring Batch : Write a List to a database table using a custom batch size

背景

我有一个 Spring 批处理 作业,其中:

  1. FlatFileItemReader - 从文件中一次读取一行
  2. ItemProcesor - 将文件中的行转换为 List<MyObject> 和 return 的 List。也就是说,文件中的每一行都被分解为 List<MyObject>(文件中的 1 行转换为许多输出行)。
  3. ItemWriter - 将 List<MyObject> 写入数据库 table。 (我用 解压缩从处理器接收到的列表的实现 并代表 JdbcBatchItemWriter)

问题

我的问题是:JdbcBatchItemWriter 不允许自定义批量大小。出于所有实际目的,该步骤的 batch-size = commit-interval。考虑到这一点,Spring Batch 中是否有另一个 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
            }
        });
    }

}

示例中的 StagingItemWriter 也是如何使用自定义 BatchPreparedStatementSetter 的示例。

的回答和评论几乎涵盖了解决方案的所有方面,是公认的答案。

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

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;
    }
}

注:

  1. Collections.unmodifiableList 的使用避免了任何显式转换的需要。
  2. 我使用 sqlFileLocation 来指定包含 sql 和 FileUtilities.getfileContents 的外部文件,只是 returns 这个 sql 文件的内容。这可以跳过,也可以在创建 bean 时直接将 sql 传递给 class。

我不会这样做。它提出了可重启性问题。相反,修改您的 reader 以生成单个项目,而不是让您的处理器接受一个对象和 return 一个列表。