首页 > 解决方案 > Spring Batch:JdbcPagingItemReader 分页

问题描述

当我做一个从 postgresql 数据库读取数据、处理和写入 csv 文件的简单批处理时遇到了一个问题。以下是读者:

 @Bean
@StepScope
public ItemReader<SearchResult> databaseReader(@Qualifier("reportingDataSource") HikariDataSource reportDataSource,
                                           @Value("#{stepExecution}") StepExecution stepExecution) {

    JdbcPagingItemReader<SearchResult> reader = new JdbcPagingItemReader<>();
    reader.setQueryProvider(createQueryProvider());
    reader.setRowMapper(new CustomRowMapper());
    reader.setDataSource(reportDataSource);
    reader.setPageSize(5);
    reader.open(stepExecution.getExecutionContext());
    return reader;
}

private PagingQueryProvider createQueryProvider(SearchTxnRequest searchTxnRequest) {
    SqlitePagingQueryProvider queryProvider = new SqlitePagingQueryProvider();
    queryProvider.setSelectClause("SELECT *");
    queryProvider.setFromClause("from dummy_table");
    queryProvider.setSortKeys(sortBy());
    return queryProvider;
}

private Map<String, Order> sortBy() {
    Map<String, Order> sortConfiguration = new HashMap<>();
    sortConfiguration.put("date", Order.ASCENDING);
    return sortConfiguration;
}

我将虚拟数据插入到“dummy_table”中,ID 不同,但其他字段中的数据相同,包括日期字段。正如您在代码中看到的,sortBy 函数定义的方式是对表格中的信息进行排序,我只选择了日期字段。问题出现在这里,如果表中的所有行都具有相同的日期,则批处理仅返回前 5 行(如果页面大小为 5)。

为什么?好吧,JdbcPagingItemReader 使用排序键分页,在这种情况下只有日期。调试显示如下:所以第一页被读取

2020-03-30 15:01:40 DEBUG [nio-7035-exec-1] o.s.b.i.database.JdbcPagingItemReader    : Reading page 0
2020-03-30 15:01:40 DEBUG [nio-7035-exec-1] o.s.b.i.database.JdbcPagingItemReader    : SQL used for reading first page: [SELECT * FROM from dummy_table ORDER BY date ASC LIMIT 5]

第二页:

2020-03-30 15:01:40 [nio-7035-exec-1] o.s.b.i.database.JdbcPagingItemReader    : Reading page 1
2020-03-30 15:01:40 [nio-7035-exec-1] o.s.b.i.database.JdbcPagingItemReader    : SQL used for reading remaining pages: [SELECT * FROM from dummy_table WHERE ((date > ?)) ORDER BY date ASC LIMIT 5]

如您所见,分页是由日期排序键进行的。我的问题是表中的所有数据都有相同的日期。明智地选择排序键以避免分页中的问题很重要。我添加了ID,这解决了问题。

标签: spring-batchreader

解决方案


您不需要调用reader.open您的databaseReader方法,如果您的阅读器被声明为ItemStreamReader. 我建议在读取器/写入器 bean 定义中返回最具体的类型,以便 Spring Batch 可以正确创建代理。在你的情况下,它应该是这样的:

@Bean
@StepScope
public JdbcPagingItemReader<SearchResult> databaseReader(@Qualifier("reportingDataSource") HikariDataSource reportDataSource) {

   JdbcPagingItemReader<SearchResult> reader = new JdbcPagingItemReader<>();
   reader.setQueryProvider(createQueryProvider());
   reader.setRowMapper(new CustomRowMapper());
   reader.setDataSource(reportDataSource);
   reader.setPageSize(5);
   return reader;
}

明智地选择排序键以避免分页中的问题很重要。我添加了ID,这解决了问题。

是的,如文档所述,这里是JdbcPagingItemReaderAbstractSqlPagingQueryProvider的 Javadocs 的摘录:

It is important to have a unique key constraint on the sort key

The columns that make up the sort key must be a true key and not just a column to order by

我相信您的日期列不是关键,这就是为什么当您添加 ID 时它可以工作。


推荐阅读