我有一个在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中设置的其他命名参数一起使用。如果没有,解决这个问题的最佳方法是什么?我需要能够为查询提供参数并对其进行分页(与使用光标相比)。谢谢!
答案 0 :(得分:1)
我通过一些激烈的调试解决了这个问题。事实证明,当MySqlPagingQueryProvider构建SQL查询以运行第一页和后续页时,它会使用方法getSortKeysWithoutAliases()
。因此,它会附加and (p.id > :_id)
而不是and (p.id > :_p.id)
。稍后,当第二页排序值创建并存储在JdbcPagingItemReader的startAfterValues
字段中时,它将使用指定的原始"p.id"
字符串,并最终将对{{1}放入命名参数映射}}。但是,当读者尝试在查询中填写_id时,它并不存在,因为读者使用了非别名删除键。
长话短说,我在定义排序键时必须删除别名引用。
("_p.id",10)
必须改为让一切都能很好地协同工作
provider.setSortKeys("p.id": Order.ASCENDING)
答案 1 :(得分:0)
我遇到了同样的问题并得到了另一种可能的解决方案。
我的表T有一个主键字段INTERNAL_ID。
JdbcPagingItemReader中的查询是这样的:
SELECT INTERNAL_ID, ... FROM T WHERE ... ORDER BY INTERNAL_ID ASC
因此,关键是:在某些情况下,查询未返回结果,然后,将上方的错误提升为<... < / p>
解决方案是:
请注意,它们有两种不同的情况:
希望这有帮助。