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



背景

我有一个Spring Batch的工作,其中:

  1. FlatFileItemReader-一次从文件中读取一行
  2. ItemProcesor-将文件中的行转换为List<MyObject>并返回List。也就是说,文件中的每一行都被分解为List<MyObject>(文件中的一行被转换为许多输出行(
  3. ItemWriter—将List<MyObject>写入数据库表。(我用过这个对从处理器接收的列表进行解包的实现并删除到JdbcBatchItemWriter(

问题

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

我的问题是:JdbcBatchItemWriter不允许自定义批量大小。出于所有实际目的,批处理大小=步骤的提交间隔。考虑到这一点,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的示例。

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

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

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只是返回这个sql文件的内容。这可以跳过,并且在创建bean时也可以直接将sql传递给类

我不会这么做。它提出了可重启性问题。相反,修改阅读器以生成单个项目,而不是让处理器接收对象并返回列表。

最新更新