将嵌入式MySQL替换为TestContainers:多数据源EntityManager适配问题
多数据源场景下TestContainers适配改造方案
核心思路
为两个数据源分别创建TestContainers MySQL容器实例,覆盖原有嵌入式数据库的DataSource配置,确保EntityManager配置类精准绑定对应容器的数据源,解决依赖注入错误问题。
步骤1:测试类中配置双容器与数据源Bean
在测试类中定义两个MySQL容器,并创建对应名称的DataSource Bean,替换原嵌入式DB的数据源配置:
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import javax.sql.DataSource; @Testcontainers @SpringBootTest public class MultiDbIntegrationTest { // Request数据库容器 @Container public static MySQLContainer<?> requestDbContainer = new MySQLContainer<>("mysql:8.0") .withDatabaseName("request_db") .withUsername("test_user") .withPassword("test_pass"); // Catalog数据库容器 @Container public static MySQLContainer<?> catalogDbContainer = new MySQLContainer<>("mysql:8.0") .withDatabaseName("catalog_db") .withUsername("test_user") .withPassword("test_pass"); // 绑定Request数据源到对应的容器 @Bean(name = "requestDataSource") public DataSource requestDataSource() { return DataSourceBuilder.create() .url(requestDbContainer.getJdbcUrl()) .username(requestDbContainer.getUsername()) .password(requestDbContainer.getPassword()) .driverClassName(requestDbContainer.getDriverClassName()) .build(); } // 绑定Catalog数据源到对应的容器 @Bean(name = "catalogDataSource") public DataSource catalogDataSource() { return DataSourceBuilder.create() .url(catalogDbContainer.getJdbcUrl()) .username(catalogDbContainer.getUsername()) .password(catalogDbContainer.getPassword()) .driverClassName(catalogDbContainer.getDriverClassName()) .build(); } }
步骤2:调整EntityManager配置类的数据源绑定
确保原有EntityManager配置类通过@Qualifier精准引用对应名称的DataSource Bean,避免依赖混淆:
RequestDBEntityManager配置
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import javax.persistence.EntityManagerFactory; @Configuration @EnableJpaRepositories( basePackages = "com.yourproject.request.repository", entityManagerFactoryRef = "requestEntityManagerFactory", transactionManagerRef = "requestTransactionManager" ) public class RequestDBEntityManager { @Autowired @Qualifier("requestDataSource") private DataSource requestDataSource; @Bean(name = "requestEntityManagerFactory") public LocalContainerEntityManagerFactoryBean requestEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(requestDataSource) .packages("com.yourproject.request.entity") .persistenceUnit("requestPU") .build(); } @Bean(name = "requestTransactionManager") public PlatformTransactionManager requestTransactionManager( @Qualifier("requestEntityManagerFactory") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); } }
CatalogEntityManager配置
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import javax.sql.DataSource; import javax.persistence.EntityManagerFactory; @Configuration @EnableJpaRepositories( basePackages = "com.yourproject.catalog.repository", entityManagerFactoryRef = "catalogEntityManagerFactory", transactionManagerRef = "catalogTransactionManager" ) public class CatalogEntityManager { @Autowired @Qualifier("catalogDataSource") private DataSource catalogDataSource; @Bean(name = "catalogEntityManagerFactory") public LocalContainerEntityManagerFactoryBean catalogEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(catalogDataSource) .packages("com.yourproject.catalog.entity") .persistenceUnit("catalogPU") .build(); } @Bean(name = "catalogTransactionManager") public PlatformTransactionManager catalogTransactionManager( @Qualifier("catalogEntityManagerFactory") EntityManagerFactory entityManagerFactory) { return new JpaTransactionManager(entityManagerFactory); } }
关键注意事项
- Bean名称一致性:测试类中DataSource的Bean名称(如
requestDataSource)必须和EntityManager配置类中@Qualifier指定的名称完全匹配,这是解决"找不到Bean"错误的核心。 - 容器复用:用
static修饰容器实例,可实现测试类间的容器复用,大幅提升测试执行效率。 - 镜像版本匹配:使用与生产环境一致的MySQL镜像版本(如
mysql:8.0),避免SQL语法或兼容性问题。 - 连接池配置:若需添加连接池参数,可在测试类的DataSource构建中补充,或通过
@ConfigurationProperties绑定配置文件中的属性。
内容的提问来源于stack exchange,提问作者Shubham Shekhar
相关产品推荐
相关产品推荐

