Spring Boot测试H2多数据源偶发Table not found错误如何解决?
问题根因
你遇到的偶发表不存在报错是典型的多初始化逻辑竞态冲突问题,核心原因有3个:
- 三重初始化逻辑并行执行:你当前配置同时触发了三种初始化操作,执行顺序完全随机:
- 两个数据源连接串中的
INIT=RUNSCRIPT指令,建立连接时就会执行data.sql - Spring Boot 默认的
DataSourceScriptDatabaseInitializer会自动加载类路径下的data.sql执行 spring.jpa.hibernate.ddl-auto=update配置会让Hibernate自动根据实体类生成表结构
当某条INSERT语句执行前,刚好另一个初始化逻辑执行了drop table操作,就会触发表不存在的报错,符合你描述的报错行不固定、偶发的特征。
- 两个数据源连接串中的
- 多数据源共用同一初始化脚本:两个H2数据源都执行包含schema1、schema2的同一套脚本,跨schema操作会出现权限或路径解析异常。
- 第二个数据源URL语法错误:
jdbc:h2:mem:dbo_security_db:database02中数据库名后的冒号是语法错误,会导致数据源解析异常,偶发跨数据源操作问题。 @DirtiesContext无效的原因是该注解仅解决上下文脏数据问题,无法修复初始化逻辑的竞态冲突。
解决步骤
1. 禁用重复初始化逻辑,统一执行入口
修改application.properties配置,关闭所有自动初始化逻辑,所有表结构、数据初始化统一由手动配置的脚本执行:
# 第一个数据库,移除INIT配置,添加H2内存库存活参数 spring.datasource.url=jdbc:h2:mem:database01;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false spring.datasource.username=user1 spring.datasource.password=abc spring.datasource.driver-class-name=org.h2.Driver # 第二个数据库,修复URL语法错误,移除INIT配置 spring.datasource.security.url=jdbc:h2:mem:dbo_security_db;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false spring.datasource.security.username=user1 spring.datasource.security.password=abc spring.datasource.security.driver-class-name=org.h2.Driver # 禁用所有自动初始化逻辑 spring.jpa.generate-ddl=false spring.jpa.hibernate.ddl-auto=none spring.sql.init.mode=never spring.jpa.properties.hibernate.format_sql=true
DB_CLOSE_DELAY=-1保证JVM运行期间H2内存库不会被自动销毁,避免测试间数据库被提前回收。
2. 拆分初始化脚本,多数据源单独配置
将原来的data.sql拆分为两个独立脚本,避免逻辑冲突:
data-database01.sql:仅保留schema1的创建、表结构、插入语句data-database02.sql:仅保留schema2的创建、表结构、插入语句
新增多数据源初始化配置类,分别给两个数据源指定对应的初始化脚本:
// 第一个数据源初始化配置 @Configuration public class DataSource1InitConfig { @Bean public DataSourceInitializer dataSource1Initializer(@Qualifier("dataSource") DataSource dataSource) { DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.addScript(new ClassPathResource("data-database01.sql")); initializer.setDatabasePopulator(populator); return initializer; } } // 第二个数据源初始化配置 @Configuration public class DataSource2InitConfig { @Bean public DataSourceInitializer dataSource2Initializer(@Qualifier("securityDataSource") DataSource dataSource) { DataSourceInitializer initializer = new DataSourceInitializer(); initializer.setDataSource(dataSource); ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.addScript(new ClassPathResource("data-database02.sql")); initializer.setDatabasePopulator(populator); return initializer; } }
注意@Qualifier中的Bean名称需要和你自己的多数据源配置中的Bean名称保持一致。
3. 测试类优化
移除@DirtiesContext注解,给测试类添加@Transactional注解,测试执行完成后自动回滚数据,避免多测试间数据互相干扰,同时大幅提升测试执行速度。
内容的提问来源于stack exchange,提问作者nikiforov.java
相关产品推荐
相关产品推荐

