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

Spring Batch升级后JpaCursorItemReader遇ResultSet关闭问题求助

Spring Batch 5.1.1升级后JpaCursorItemReader事务关闭ResultSet问题

背景

我们正将基于Spring Boot的批处理应用的Spring Batch和Spring Boot框架从4.3.4版本升级至5.1.1版本。此前使用HibernateCursorItemReader结合查询提供者从多表获取数据。

在5.1.1版本中,HibernateCursorItemReader已被废弃并标记为待移除,Spring Batch文档推荐使用JpaCursorItemReader替代,Step配置如下:

@Bean
public Step processRecords(MyStepConfig stepConfig) {
  return new StepBuilder("MyStep", this.jobRepository)
      .<SourceRecord, ProcessedRecord>chunk(1000, this.platformTransactionManager)
      .reader(stepConfig.readRecords())
      .processor(stepConfig.processItems())
      .writer(stepConfig.writeItems())
      .listener(stepConfig.runListener())
      .build();
}

问题现象

使用JpaCursorItemReader时,读取并成功处理完第一块1000条记录后,阅读器抛出如下错误:

Caused by: java.sql.SQLException: You can't operate on a closed ResultSet!!!
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
    at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
    at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:691)
    at org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl.advanceNext(JdbcValuesResultSetImpl.java:218)
    ... 62 common frames omitted
Caused by: java.lang.NullPointerException: Cannot invoke "java.sql.ResultSet.next()" because "this.inner" is null
    at com.mchange.v2.c3p0.impl.NewProxyResultSet.next(NewProxyResultSet.java:685)
    ... 63 common frames omitted

推测是第一块事务提交导致ResultSet被关闭。了解到一种潜在解决方案是使用不参与事务的独立数据源绕过事务管理器,但不知如何在Hibernate中实现(我们无需事务,因为应用不更新数据库记录)。

使用JpaPagingItemReader无此问题,但性能远不如从前。

已尝试方案

  • 设置查询提示org.hibernate.readOnly = true使Hibernate处于只读模式
  • 使用不同的事务管理器(ResourcelessTransactionManager)

补充代码说明

阅读器类继承自JpaCursorItemReader,并通过QueryProvider设置查询,代码如下:

public abstract class BaseHibernateItemReader<T, S extends HibernateProperties>
    extends JpaCursorItemReader<T> {

  [...]

  protected void setQueryProvider(String databaseName) throws ActaporterConfigurationException {
    Properties properties =
        this.adapterConfigurationProperties.getHibernateProperties(databaseName);

    // 聚合配置文件和实体类注解创建Hibernate配置
    HibernateConfig configuration = new HibernateConfig();
    configuration.addProperties(properties);
    configuration.scanPackages(
        (String) properties.get(HibernateProperties.PROPERTY_PACKAGES_TO_SCAN));

    SessionFactory sessionFactory = configuration.buildSessionFactory();

    setEntityManagerFactory(sessionFactory);

    CriteriaBuilder builder = sessionFactory.getCriteriaBuilder();
    CriteriaQuery<T> query = builder.createQuery(this.entityClass);
    Root<T> root = query.from(this.entityClass);
    query.select(root);

    // 添加自定义where条件
    Predicate[] p = getWhereClause(builder, root);
    if (p != null) {
      query.where(p);
    }

    // 添加自定义排序
    List<Order> o = getOrderBy(builder, root);
    if (o != null) {
      query.orderBy(o);
    }

    // 为JpaCursorItemReader设置QueryProvider
    setQueryProvider(new JpaCriteriaQueryProvider<>(query));
  }

  [...]

}
class JpaCriteriaQueryProvider<T> extends AbstractJpaQueryProvider {
  private final CriteriaQuery<T> query;

  public JpaCriteriaQueryProvider(CriteriaQuery<T> query) {
    this.query = query;
  }

  @Override
  public Query createQuery() {
    return getEntityManager().createQuery(this.query);
  }

  @Override
  public void afterPropertiesSet() {}
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 11:57:05