小编典典

使用sortKeys和参数值的Spring Batch分页

spring-boot

我在Spring Boot中运行的Spring
Batch项目运行得很好。对于我的读者,我将JdbcPagingItemReader与MySqlPagingQueryProvider一起使用。

@Bean
public ItemReader<Person> reader(DataSource dataSource) {
    MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider()
    provider.setSelectClause(ScoringConstants.SCORING_SELECT_STATEMENT)
    provider.setFromClause(ScoringConstants.SCORING_FROM_CLAUSE)
    provider.setSortKeys("p.id": Order.ASCENDING)

    JdbcPagingItemReader<Person> reader = new JdbcPagingItemReader<Person>()
    reader.setRowMapper(new PersonRowMapper())
    reader.setDataSource(dataSource)
    reader.setQueryProvider(provider)
    //Setting these caused the exception
    reader.setParameterValues(
        startDate: new Date() - 31,
        endDate: new Date()
    ) 
    reader.afterPropertiesSet()
    return reader
}

但是,当我使用一些命名参数修改查询以替换以前的硬编码日期值并在读取器上设置这些参数值(如上所示)时,在读取的 第二
页上出现以下异常(第一页工作正常,因为_id参数分页查询提供者尚未使用):

org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter '_id': No value registered for key '_id'
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:336)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.getPreparedStatementCreator(NamedParameterJdbcTemplate.java:374)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:192)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:199)
at org.springframework.batch.item.database.JdbcPagingItemReader.doReadPage(JdbcPagingItemReader.java:218)
at org.springframework.batch.item.database.AbstractPagingItemReader.doRead(AbstractPagingItemReader.java:108)

这是SQL的示例,默认情况下不包含WHERE子句。阅读第二页时,会自动创建一个:

select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id)  from person p

在第二页上,将sql修改为以下内容,但是似乎未提供_id的命名参数:

select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id) from person p WHERE id > :_id

我想知道我是否不能将MySqlPagingQueryProvider排序键与JdbcPagingItemReader中设置的其他命名参数一起使用。如果不是,那么解决此问题的最佳选择是什么?我需要能够为查询提供参数并对其进行分页(与使用游标相比)。谢谢!


阅读 1063

收藏
2020-05-30

共1个答案

小编典典

我通过一些密集的调试解决了这个问题。事实证明,MySqlPagingQueryProvider
getSortKeysWithoutAliases()在建立要在第一页和后续页运行的SQL查询时使用了一种方法。因此,它附加and (p.id > :_id)而不是and (p.id > :_p.id)。稍后,当创建第二个页面排序值并将其存储在JdbcPagingItemReader的startAfterValues字段中时,它将使用"p.id"指定的原始String并最终将该对放入命名参数映射中("_p.id",10)。但是,当读者尝试在查询中填写_id时,它不存在,因为读者使用了非别名删除键。

长话短说,在定义排序键时,我必须删除别名引用。

provider.setSortKeys("p.id": Order.ASCENDING)

必须更改为,才能使所有组件正常工作

provider.setSortKeys("id": Order.ASCENDING)

2020-05-30