一、基础多数据源配置
1. 添加依赖
1 | org.springframework.bootspring-boot-starter-data-jpamysqlmysql-connector-java |
2. 配置多个数据源
1 2 3 4 5 6 7 8 9 10 11 | # 主数据源 spring.datasource.primary.url=jdbc : mysql : //localhost : 3306/db1 spring.datasource.primary.username=root spring.datasource.primary.password=123456 spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver # 次数据源 spring.datasource.secondary.url=jdbc : mysql : //localhost : 3306/db2 spring.datasource.secondary.username=root spring.datasource.secondary.password=123456 spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver |
3. 配置数据源Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Configuration public class DataSourceConfig { // 主数据源 @Bean @Primary @ConfigurationProperties (prefix= "spring.datasource.primary" ) public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } // 次数据源 @Bean @ConfigurationProperties (prefix= "spring.datasource.secondary" ) public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } } |
二、JPA多数据源配置
1. 配置主数据源JPA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | @Configuration @EnableTransactionManagement @EnableJpaRepositories ( basePackages = "com.example.repository.primary" , entityManagerFactoryRef = "primaryEntityManagerFactory" , transactionManagerRef = "primaryTransactionManager" ) public class PrimaryJpaConfig { @Autowired @Qualifier ( "primaryDataSource" ) private DataSource primaryDataSource; @Primary @Bean public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(primaryDataSource) .packages( "com.example.entity.primary" ) .persistenceUnit( "primaryPersistenceUnit" ) .properties(jpaProperties()) .build(); } private Map jpaProperties() { Map props = new HashMap(); props.put( "hibernate.hbm2ddl.auto" , "update" ); props.put( "hibernate.dialect" , "org.hibernate.dialect.MySQL8Dialect" ); return props; } @Primary @Bean public PlatformTransactionManager primaryTransactionManager( @Qualifier ( "primaryEntityManagerFactory" ) EntityManagerFactory emf) { return new JpaTransactionManager(emf); } } |
2. 配置次数据源JPA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | @Configuration @EnableTransactionManagement @EnableJpaRepositories ( basePackages = "com.example.repository.secondary" , entityManagerFactoryRef = "secondaryEntityManagerFactory" , transactionManagerRef = "secondaryTransactionManager" ) public class SecondaryJpaConfig { @Autowired @Qualifier ( "secondaryDataSource" ) private DataSource secondaryDataSource; @Bean public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(secondaryDataSource) .packages( "com.example.entity.secondary" ) .persistenceUnit( "secondaryPersistenceUnit" ) .properties(jpaProperties()) .build(); } private Map jpaProperties() { Map props = new HashMap(); props.put( "hibernate.hbm2ddl.auto" , "update" ); props.put( "hibernate.dialect" , "org.hibernate.dialect.MySQL8Dialect" ); return props; } @Bean public PlatformTransactionManager secondaryTransactionManager( @Qualifier ( "secondaryEntityManagerFactory" ) EntityManagerFactory emf) { return new JpaTransactionManager(emf); } } |
三、MyBatis多数据源配置
1. 主数据源配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @Configuration @MapperScan ( basePackages = "com.example.mapper.primary" , sqlSessionFactoryRef = "primarySqlSessionFactory" ) public class PrimaryMyBatisConfig { @Primary @Bean @ConfigurationProperties (prefix = "spring.datasource.primary" ) public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean public SqlSessionFactory primarySqlSessionFactory( @Qualifier ( "primaryDataSource" ) DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources( "classpath:mapper/primary/*.xml" )); return sessionFactory.getObject(); } @Primary @Bean public SqlSessionTemplate primarySqlSessionTemplate( @Qualifier ( "primarySqlSessionFactory" ) SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } |
2. 次数据源配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | @Configuration @MapperScan ( basePackages = "com.example.mapper.secondary" , sqlSessionFactoryRef = "secondarySqlSessionFactory" ) public class SecondaryMyBatisConfig { @Bean @ConfigurationProperties (prefix = "spring.datasource.secondary" ) public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } @Bean public SqlSessionFactory secondarySqlSessionFactory( @Qualifier ( "secondaryDataSource" ) DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setMapperLocations( new PathMatchingResourcePatternResolver() .getResources( "classpath:mapper/secondary/*.xml" )); return sessionFactory.getObject(); } @Bean public SqlSessionTemplate secondarySqlSessionTemplate( @Qualifier ( "secondarySqlSessionFactory" ) SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } |
四、动态数据源配置(运行时切换)
1. 抽象路由数据源
1 2 3 4 5 6 7 | public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } } |
2. 数据源上下文持有者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class DataSourceContextHolder { private static final ThreadLocal contextHolder = new ThreadLocal(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } } |
3. 配置动态数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | @Configuration public class DynamicDataSourceConfig { @Bean @ConfigurationProperties (prefix= "spring.datasource.primary" ) public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties (prefix= "spring.datasource.secondary" ) public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } @Primary @Bean public DataSource dynamicDataSource( @Qualifier ( "primaryDataSource" ) DataSource primaryDataSource, @Qualifier ( "secondaryDataSource" ) DataSource secondaryDataSource) { Map<object data-origwidth= "" data-origheight= "" style= "width: 1264px;" > targetDataSources = new HashMap(); targetDataSources.put( "primary" , primaryDataSource); targetDataSources.put( "secondary" , secondaryDataSource); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(primaryDataSource); return dynamicDataSource; } } </object> |
4. 使用AOP切换数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | @Aspect @Component public class DataSourceAspect { @Pointcut ( "@annotation(com.example.annotation.TargetDataSource)" ) public void dataSourcePointCut() {} @Before ( "dataSourcePointCut()" ) public void before(JoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); TargetDataSource ds = method.getAnnotation(TargetDataSource. class ); if (ds == null ) { DataSourceContextHolder.setDataSourceType( "primary" ); } else { DataSourceContextHolder.setDataSourceType(ds.value()); } } @After ( "dataSourcePointCut()" ) public void after(JoinPoint point) { DataSourceContextHolder.clearDataSourceType(); } } |
5. 自定义注解
1 2 3 4 5 6 | @Target ({ElementType.METHOD, ElementType.TYPE}) @Retention (RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value() default "primary" ; } |
6. 使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Service public class UserService { @Autowired private UserMapper userMapper; // 使用主数据源 public User getPrimaryUser(Long id) { return userMapper.selectById(id); } // 使用次数据源 @TargetDataSource ( "secondary" ) public User getSecondaryUser(Long id) { return userMapper.selectById(id); } } |
五、多数据源事务管理
1. JTA分布式事务(Atomikos)
1 | org.springframework.bootspring-boot-starter-jta-atomikos |
2. 配置JTA数据源
1 2 3 4 5 6 7 8 9 10 11 12 13 | # 主数据源 spring.jta.atomikos.datasource.primary.unique-resource-name=primaryDS spring.jta.atomikos.datasource.primary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource spring.jta.atomikos.datasource.primary.xa-properties.url=jdbc : mysql : //localhost : 3306/db1 spring.jta.atomikos.datasource.primary.xa-properties.user=root spring.jta.atomikos.datasource.primary.xa-properties.password=123456 # 次数据源 spring.jta.atomikos.datasource.secondary.unique-resource-name=secondaryDS spring.jta.atomikos.datasource.secondary.xa-data-source-class-name=com.mysql.cj.jdbc.MysqlXADataSource spring.jta.atomikos.datasource.secondary.xa-properties.url=jdbc : mysql : //localhost : 3306/db2 spring.jta.atomikos.datasource.secondary.xa-properties.user=root spring.jta.atomikos.datasource.secondary.xa-properties.password=123456 |
3. 使用分布式事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @Service public class OrderService { @Transactional // 跨数据源事务 public void placeOrder(Order order) { // 操作主数据源 primaryRepository.save(order); // 操作次数据源 auditRepository.logOrder(order); // 如果此处抛出异常,两个操作都会回滚 } } |
六、最佳实践
-
命名规范:
- 为每个数据源使用清晰的命名(如customerDS, orderDS)
- 包结构按数据源分离(com.example.repository.primary / .secondary)
-
连接池配置:
1 2 | spring.datasource.primary.hikari.maximum-pool-size=10 spring.datasource.secondary.hikari.maximum-pool-size=5 |
-
监控指标:
- 为每个数据源配置独立的监控
- 使用Spring Actuator暴露数据源健康指标
-
性能考虑:
- 高频访问的数据源使用更大的连接池
- 读写分离场景考虑主从数据源
-
测试策略:
- 为每个数据源编写独立的测试类
- 测试跨数据源事务的回滚行为
七、常见问题解决
问题1:循环依赖
1 2 3 4 5 6 | // 解决方法:使用@DependsOn @Bean @DependsOn ( "dynamicDataSource" ) public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } |
问题2:MyBatis缓存冲突
1 2 3 4 5 6 7 | // 解决方法:为每个SqlSessionFactory配置独立的缓存环境 sqlSessionFactory.setConfiguration(configuration); configuration.setEnvironment( new Environment( "primaryEnv" , transactionFactory, dataSource )); |
问题3:事务传播行为异常
1 2 3 | // 解决方法:明确指定事务管理器 @Transactional (transactionManager = "primaryTransactionManager" ) public void primaryOperation() {...} |
通过以上配置,Spring Boot应用可以灵活地支持多数据源场景,无论是简单的多库连接还是复杂的动态数据源切换需求。根据实际业务场景选择最适合的配置方式,并注意事务管理和性能调优。
以上就是SpringBoot多数据源配置完整指南的详细内容,更多关于SpringBoot多数据源配置的资料请关注IT俱乐部其它相关文章!