运行Spring Batch应用提示HikariDataSource已关闭该如何解决?
异常含义
这个异常是Spring Batch在更新Job/Step执行状态、需要开启事务时,无法从Hikari连接池获取JDBC连接,因为底层的Hikari连接池已经被关闭,最终导致事务创建失败。
常见触发原因
- 嵌入式H2内存库默认特性导致:当前你使用的是内存模式H2,默认配置下如果所有数据库连接都被释放超过一定时间,H2会自动销毁内存数据库实例,连带触发连接池关闭。而你用了异步线程池执行批处理任务,很可能出现批处理任务还没跑完,所有临时连接被回收触发H2关闭的情况。
- Spring Bean销毁时序问题:如果触发批处理的主逻辑提前执行完成,Spring上下文会启动Bean销毁流程,DataSource会优先被销毁,而此时异步线程中仍有未完成的批处理任务,需要访问数据库更新执行状态时就会抛出该异常。
- Hikari连接池配置和H2不兼容:默认Hikari的空闲连接回收策略比较激进,短时间没有活跃连接就会回收所有空闲连接,触发H2自动关闭逻辑。
- Liquibase执行异常:如果Liquibase初始化表结构时出现非致命错误,可能触发数据源重连逻辑,原有连接池被关闭后没有正确重建。
可调整的解决配置
H2数据库配置调整
修改H2连接串,添加禁止无连接时自动关闭的参数,调整后的配置如下:
spring.datasource.url=jdbc:h2:mem:springbatch;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1
其中DB_CLOSE_DELAY=-1表示H2永远不会因为没有活跃连接而自动关闭。
Hikari连接池配置调整
添加以下配置避免连接被过度回收:
# 最小空闲连接数,保证始终有存活连接避免H2被关闭 spring.datasource.hikari.minimum-idle=2 # 最大连接池大小,匹配批处理的并发任务数即可 spring.datasource.hikari.maximum-pool-size=10 # 空闲连接超时时间,设置为10分钟,避免频繁回收 spring.datasource.hikari.idle-timeout=600000 # 连接最大生命周期,设置为30分钟 spring.datasource.hikari.max-lifetime=1800000
异步执行时序调整
对你使用的taskExecutor添加关闭策略配置,确保所有任务跑完再销毁Bean:
@Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(8); executor.setMaxPoolSize(16); executor.setQueueCapacity(100); // 关闭时等待所有任务完成 executor.setWaitForTasksToCompleteOnShutdown(true); // 最长等待时间,按你的批处理最大执行时间设置 executor.setAwaitTerminationSeconds(3600); return executor; }
如果是单次执行的批处理任务,需要在提交Job后等待执行完成,不要让主程序提前退出。
切换MySQL的可行性说明
切换MySQL是可行的解决方案,和该问题存在直接关联:
嵌入式H2本身定位是开发测试环境的轻量数据库,不适合生产环境长时间运行、有异步批处理的场景,其内存实例和应用绑定的特性很容易和异步任务产生时序冲突。切换为独立部署的MySQL后,数据库生命周期和应用完全解耦,不存在应用还需要连接时数据库实例被销毁的问题,同时MySQL的连接稳定性更高,能从根本上避免这类异常。如果仅需要在开发环境解决问题,无需切换MySQL,调整上述配置即可解决。
内容的提问来源于stack exchange,提问作者AndreaNobili
相关产品推荐
相关产品推荐

