子线程用HikariPool连接执行DB操作失败:提示未选中数据库
解决子线程中执行Repository操作抛出“No database selected”的问题
问题原因
Spring的数据库连接、事务上下文都是通过ThreadLocal绑定到当前线程的。主线程中,Spring会自动完成连接获取、数据库选择等初始化工作;但你手动创建的ExecutorService不属于Spring管理的线程池,子线程无法继承或初始化这些ThreadLocal上下文,导致执行Repository操作时找不到已配置的数据库连接,从而抛出“No database selected”错误。
解决方案
方案1:使用Spring管理的线程池替代手动创建的ExecutorService
不要自己用Executors.newSingleThreadExecutor()创建线程池,改用Spring提供的ThreadPoolTaskExecutor,它会自动为子线程初始化数据库连接上下文。
第一步,配置Spring线程池:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; @Configuration public class ThreadPoolConfig { @Bean(name = "customDbExecutor") public Executor customDbExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(1); executor.setMaxPoolSize(1); executor.setQueueCapacity(10); executor.setThreadNamePrefix("DbTask-"); executor.initialize(); return executor; } }
第二步,修改你的RTest类,注入Spring线程池:
import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import java.util.concurrent.Executor; @Component @RequiredArgsConstructor public class RTest { private final SuiteResultRepository suiteResultRepository; private final Executor customDbExecutor; public void callInNewThread() { customDbExecutor.submit(() -> { try { suiteResultRepository.get(1L); } catch (Exception e) { e.printStackTrace(); } }); } }
方案2:使用@Async注解简化异步操作
如果不需要自定义线程池的复杂配置,可以用Spring的@Async注解实现异步调用,只需两步:
- 在Spring启动类上添加
@EnableAsync注解,开启异步支持:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
- 修改
RTest类,用@Async标记异步方法:
import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor public class RTest { private final SuiteResultRepository suiteResultRepository; @Async public void callInNewThread() { try { suiteResultRepository.get(1L); } catch (Exception e) { e.printStackTrace(); } } }
方案3:手动绑定数据库连接(不推荐,仅特殊场景使用)
如果必须使用自定义线程池,可以在子线程中手动获取数据库连接并绑定,或者通过TransactionTemplate确保操作在事务上下文内执行:
import lombok.RequiredArgsConstructor; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.stereotype.Component; import javax.sql.DataSource; import java.sql.Connection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Component @RequiredArgsConstructor public class RTest { private final SuiteResultRepository suiteResultRepository; private final DataSource dataSource; private final ExecutorService executorService = Executors.newSingleThreadExecutor(); public void callInNewThread() { executorService.submit(() -> { Connection conn = null; try { // 手动获取并绑定连接到当前线程 conn = DataSourceUtils.getConnection(dataSource); suiteResultRepository.get(1L); } catch (Exception e) { e.printStackTrace(); } finally { // 释放连接 if (conn != null) { DataSourceUtils.releaseConnection(conn, dataSource); } } }); } }
注意:这种方式需要手动管理连接的获取和释放,容易出现连接泄漏问题,除非特殊场景,优先选择方案1或2。
内容的提问来源于stack exchange,提问作者pavan kumar
相关产品推荐
相关产品推荐

