You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch多线程步骤中JdbcPagingItemReader无数据时排序键报错及setSaveState作用

问题

使用Spring Batch 4.3.5开发批处理应用,在多线程步骤中使用JdbcPagingItemReader时,当查询无数据会触发如下错误:

org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter '_PIN_SEQ_ID': No value registered for key '_PIN_SEQ_ID' 
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:354) 
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.getPreparedStatementCreator(NamedParameterJdbcTemplate.java:418) 
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.getPreparedStatementCreator(NamedParameterJdbcTemplate.java:392)

Reader代码如下,添加reader.setSaveState(false)前会触发上述错误:

public JdbcPagingItemReader<BatchUserHistoryMVDO> processJobReader(
        @Value("#{jobExecutionContext[batchId]}") long batchId) {
    final JdbcPagingItemReader<BatchUserHistoryMVDO> reader = new JdbcPagingItemReader<>();
    final BatchUserHistoryMVMapper userHistoryMVMapper = new BatchUserHistoryMVMapper();
    reader.setDataSource(this.dataSource.getSecondaryDataSource());
    reader.setFetchSize(this.configProperties.getProcessPageSize());
    reader.setPageSize(this.configProperties.getProcessPageSize());
    reader.setRowMapper(userHistoryMVMapper);
    reader.setQueryProvider(createQuery());
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("id", batchId);
    reader.setParameterValues(parameters);
    return reader;
}

private AbstractSqlPagingQueryProvider createQuery() {
    final Map<String, Order> sortKeys = new HashMap<>();
    sortKeys.put("PIN_SEQ_ID", Order.ASCENDING);
    final AbstractSqlPagingQueryProvider queryProvider = new
            OraclePagingQueryProvider();
    queryProvider.setSelectClause("*");
    queryProvider.setFromClause("from POA01.BATCH_USER_HISTORY_MV where batch_id = :id");
    queryProvider.setSortKeys(sortKeys);
    return queryProvider;
}

添加reader.setSaveState(false)后批处理可正常完成,请问该方法在此场景中起到什么作用?未设置时为何会因排序键导致报错?

解答

setSaveState(false)的作用

  • 关闭ItemReader的状态持久化:Spring Batch默认会将ItemReader的读取状态(比如分页查询的当前位置、排序键的最后取值等)保存到Job Execution Context,用于任务重启时恢复读取进度。设置setSaveState(false)后,Reader不再保存或恢复任何状态,每次运行都从头开始读取。
  • 规避多线程状态冲突:在多线程步骤中,多个Reader实例会共享同一Job Execution Context的状态,关闭状态持久化后,每个线程的Reader独立运行,不会互相干扰。

未设置时报错的原因

当查询无数据时,JdbcPagingItemReader第一次执行查询返回空结果集,但因为默认开启状态保存(saveState=true),它会尝试将排序键PIN_SEQ_ID的"最后取值"保存到上下文。由于没有数据,这个排序键没有对应的取值,后续Spring Batch在处理状态时,错误地将排序键当作SQL参数(自动添加前缀_变成_PIN_SEQ_ID)去查找参数值,但你仅设置了:id参数,没有_PIN_SEQ_ID的参数值,因此触发InvalidDataAccessApiUsageException。

简单来说,空结果集导致状态保存逻辑异常,把排序键误判为未提供的SQL参数,关闭状态保存后就不会触发这个异常流程了。

内容的提问来源于stack exchange,提问作者tzaman3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 09:55:22