在Spring Boot应用中配置多数据源(Multiple MySQL DataSources)(配置.Boot.Spring.DataSources.MySQL...)

wufei123 发布于 2025-09-11 阅读(1)
配置多数据源需为每个数据源独立定义连接属性、数据源实例、实体管理器工厂和事务管理器,通过@Primary标注主数据源,@EnableJpaRepositories指定各自包路径实现隔离,确保事务管理器与数据源一一对应,并在@Service中用@Transactional("xxxTransactionManager")显式指定事务管理器以保障事务独立性。

在spring boot应用中配置多数据源(multiple mysql datasources)

在Spring Boot应用中配置多数据源,核心在于为每个数据源独立定义其连接属性、数据源实例、JPA实体管理器工厂以及事务管理器。这通常通过创建多个配置类来实现,每个类负责一个数据源的完整生命周期管理,并确保实体和仓库(Repository)能正确地与各自的数据源关联起来。这能让一个应用同时处理来自不同数据库的数据,例如,一个用于核心业务,另一个用于历史数据或报表。

解决方案

要在Spring Boot应用中配置多个MySQL数据源,我们需要以下几个步骤。这不仅仅是把配置堆在一起,更重要的是理清各个组件之间的依赖和隔离。

首先,确保你的

pom.xml
里有必要的依赖,主要是
spring-boot-starter-data-jpa
mysql-connector-java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

接着,在

application.properties
application.yml
中定义两个数据源的连接信息。这里我用
primary
secondary
来区分。
# Primary DataSource Configuration
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db?useSSL=false&serverTimezone=UTC
spring.datasource.primary.username=root
spring.datasource.primary.password=password
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.primary.hikari.maximum-pool-size=10
spring.datasource.primary.hikari.minimum-idle=5

# Secondary DataSource Configuration
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db?useSSL=false&serverTimezone=UTC
spring.datasource.secondary.username=root
spring.datasource.secondary.password=password
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.hikari.maximum-pool-size=10
spring.datasource.secondary.hikari.minimum-idle=5

# JPA properties for Primary
spring.jpa.primary.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.primary.hibernate.ddl-auto=update
spring.jpa.primary.show-sql=true

# JPA properties for Secondary
spring.jpa.secondary.database-platform=org.hibernate.dialect.MySQL8Dialect
spring.jpa.secondary.hibernate.ddl-auto=update
spring.jpa.secondary.show-sql=true

然后,我们需要创建两个独立的配置类,分别管理

primary
secondary
数据源。

PrimaryDataSourceConfig.java:

package com.example.multids.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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 org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    entityManagerFactoryRef = "primaryEntityManagerFactory",
    transactionManagerRef = "primaryTransactionManager",
    basePackages = {"com.example.multids.primary.repository"} // 确保扫描Primary数据源的Repository
)
public class PrimaryDataSourceConfig {

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary")
    public DataSourceProperties primaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary.hikari")
    public DataSource primaryDataSource(@Qualifier("primaryDataSourceProperties") DataSourceProperties properties) {
        return primaryDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean(name = "primaryEntityManagerFactory")
    @Primary
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("primaryDataSource") DataSource dataSource) {
        Map<String, String> jpaProperties = new HashMap<>();
        jpaProperties.put("hibernate.hbm2ddl.auto", "update");
        jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        jpaProperties.put("hibernate.show_sql", "true"); // 方便调试

        return builder
                .dataSource(dataSource)
                .properties(jpaProperties)
                .packages("com.example.multids.primary.entity") // 确保扫描Primary数据源的实体
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    @Bean(name = "primaryTransactionManager")
    @Primary
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory) {
        return new JpaTransactionManager(primaryEntityManagerFactory.getObject());
    }
}

SecondaryDataSourceConfig.java:

package com.example.multids.config;

import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    entityManagerFactoryRef = "secondaryEntityManagerFactory",
    transactionManagerRef = "secondaryTransactionManager",
    basePackages = {"com.example.multids.secondary.repository"} // 确保扫描Secondary数据源的Repository
)
public class SecondaryDataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.secondary")
    public DataSourceProperties secondaryDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.secondary.hikari")
    public DataSource secondaryDataSource(@Qualifier("secondaryDataSourceProperties") DataSourceProperties properties) {
        return secondaryDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
    }

    @Bean(name = "secondaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("secondaryDataSource") DataSource dataSource) {
        Map<String, String> jpaProperties = new HashMap<>();
        jpaProperties.put("hibernate.hbm2ddl.auto", "update");
        jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        jpaProperties.put("hibernate.show_sql", "true");

        return builder
                .dataSource(dataSource)
                .properties(jpaProperties)
                .packages("com.example.multids.secondary.entity") // 确保扫描Secondary数据源的实体
                .persistenceUnit("secondaryPersistenceUnit")
                .build();
    }

    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryEntityManagerFactory") LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory) {
        return new JpaTransactionManager(secondaryEntityManagerFactory.getObject());
    }
}

关键点:

  • DataSourceProperties
    用于从配置文件读取数据源属性。
  • @ConfigurationProperties
    注解将配置文件中的属性绑定到对应的Bean上。
  • @Primary
    注解标记主数据源,当Spring需要自动注入
    DataSource
    EntityManagerFactory
    时,会优先选择它。非主数据源则需要通过
    @Qualifier
    明确指定。
  • @EnableJpaRepositories
    注解需要指定
    entityManagerFactoryRef
    transactionManagerRef
    ,以及
    basePackages
    来告诉Spring扫描哪些包下的Repository属于这个数据源。
  • LocalContainerEntityManagerFactoryBean
    用于创建JPA的
    EntityManagerFactory
    ,它需要指定数据源和实体类所在的包。
  • JpaTransactionManager
    用于管理事务,它需要关联到对应的
    EntityManagerFactory

最后,确保你的实体和Repository分别放在对应的包下,例如:

  • com.example.multids.primary.entity
  • com.example.multids.primary.repository
  • com.example.multids.secondary.entity
  • com.example.multids.secondary.repository

这样,当你在服务层使用Repository时,Spring就能根据配置正确地路由到对应的数据源。

为什么我的Spring Boot应用需要配置多个数据源?

这事儿吧,说起来也挺常见的。你可能一开始没想过,但项目一复杂,各种历史包袱、业务隔离、性能考量就都冒出来了。配置多个数据源,通常不是为了“炫技”,而是为了解决实际问题。

我个人觉得,最常见的场景就是遗留系统集成。你想啊,一个新项目用Spring Boot开发,但很多老数据还在一个老旧的数据库里,你又不能一下把所有数据都迁移过来,或者说,迁移的成本和风险太高。这时候,配置一个额外的数据源去读写老数据库,就成了最稳妥的方案。

再来就是业务隔离和数据安全。有些时候,不同业务模块的数据,在逻辑上就应该分开,甚至可能因为合规性要求,需要物理隔离。比如,用户敏感信息可能放在一个高安全级别的数据库里,而一些公开的商品信息则放在另一个数据库。这样即使一个数据库出了问题,也不会影响到所有数据。

还有就是性能优化和分库分表。虽然Spring Boot本身不直接提供分库分表的能力,但多数据源是实现这些高级策略的基础。当你的数据量大到单个数据库无法承受时,你可能会把数据分散到多个数据库中,这时候你的应用就需要同时连接并管理这些分散的数据源。或者,你可能有一个高并发写入的业务,需要一个专门的数据库,而另一个数据库则用于低频的报表查询,它们对数据库的配置和优化方向是完全不同的。

总的来说,多数据源的配置,往往是业务发展到一定阶段,对数据管理和系统架构提出更高要求时的必然选择。它不是为了让系统变得更复杂,而是为了让系统在面对复杂需求时,能保持灵活性和健壮性。

PIA PIA

全面的AI聚合平台,一站式访问所有顶级AI模型

PIA226 查看详情 PIA 配置多数据源时,有哪些常见的“坑”需要特别注意?

我跟你说,这玩意儿配置起来,看似一套流程,但真要跑起来,各种稀奇古怪的问题就来了。我之前就遇到过好几次,搞得我焦头烂额。

首先,事务管理是最大的一个“坑”。Spring的

@Transactional
注解默认只对一个数据源生效。如果你不明确指定,它可能会默认作用到主数据源上,导致你对第二个数据源的操作根本没有事务保护,或者事务管理混乱。比如,你在一个方法里同时操作了两个数据源,如果其中一个失败了,另一个数据源的操作却已经提交了,那数据一致性就彻底崩了。解决办法是,在
@Transactional
注解中明确指定要使用的事务管理器,比如
@Transactional("secondaryTransactionManager")

其次,实体和仓库的扫描范围。

@EnableJpaRepositories
@EntityScan
(或者在
LocalContainerEntityManagerFactoryBean
中配置
packagesToScan
)的
basePackages
参数是至关重要的。如果你不小心把所有实体和仓库都放在一个包下,或者扫描范围设置得太广,Spring可能会尝试用一个
EntityManagerFactory
去管理所有实体,导致实体管理器冲突,或者某些实体根本无法被正确识别。每个数据源的配置都必须精确地指向它自己的实体和仓库所在的包。

再者,Spring Boot的自动配置干扰。Spring Boot非常“聪明”,它会尝试自动配置一个数据源。当我们手动配置多个数据源时,就可能和它的自动配置发生冲突。通常,通过明确定义所有必要的Bean并使用

@Primary
注解来指定主数据源,可以有效避免这种冲突。有时甚至需要显式排除
DataSourceAutoConfiguration
,但这在大多数情况下并非必须,只要你正确定义了所有数据源的Bean。

还有,连接池的独立配置。每个数据源都会有自己的连接池(比如HikariCP)。你需要在

application.properties
中为每个数据源独立配置连接池的参数,比如最大连接数、最小空闲连接数、连接超时时间等。如果一个数据源的连接池配置不当,比如最大连接数太小,在高并发场景下就可能出现连接耗尽,导致应用卡死。

最后,一个比较隐蔽的“坑”是,如果你在某个服务方法里,不小心混用了来自不同数据源的Repository,并且没有正确管理事务,那么就很容易出现问题。我建议,尽量让处理某个特定数据源的业务逻辑,集中在它自己的服务层中,避免在同一个方法里跨数据源进行复杂操作,除非你真的清楚自己在做什么,并且已经为之设计了完善的事务策略。

如何确保不同数据源的事务独立且正确地工作?

事务这块,是多数据源配置里最容易出岔子,也最关键的地方。搞不好,数据一致性就没了。要确保不同数据源的事务独立且正确地工作,核心在于显式指定和隔离。

首先,每个数据源必须拥有自己独立的事务管理器。这是基础中的基础。在我们的配置中,我们为

primary
数据源定义了
primaryTransactionManager
,为
secondary
数据源定义了
secondaryTransactionManager
。这些事务管理器是基于各自的
EntityManagerFactory
创建的,因此它们天生就是隔离的。一个事务管理器只能管理它所关联的那个数据源的事务。

其次,也是最关键的一步,就是在你的业务逻辑层,也就是通常的Service层方法上,使用

@Transactional
注解时,明确指定要使用的事务管理器。

比如,如果你有一个操作

primary
数据库的服务方法:
@Service
public class PrimaryService {
    // ... 注入 PrimaryRepository

    @Transactional("primaryTransactionManager") // 明确指定使用Primary数据源的事务管理器
    public void performPrimaryDbOperation(PrimaryEntity entity) {
        // ... 对Primary数据源的操作
    }
}

而对于操作

secondary
数据库的服务方法:
@Service
public class SecondaryService {
    // ... 注入 SecondaryRepository

    @Transactional("secondaryTransactionManager") // 明确指定使用Secondary数据源的事务管理器
    public void performSecondaryDbOperation(SecondaryEntity entity) {
        // ... 对Secondary数据源的操作
    }
}

这样,当

performPrimaryDbOperation
方法被调用时,Spring就会使用
primaryTransactionManager
来管理事务,它的提交或回滚只影响
primary_db
。同理,
performSecondaryDbOperation
方法只会影响
secondary_db
。它们之间互不干涉,实现了事务的独立性。

这里需要特别强调的是,除非你引入了像JTA(Java Transaction API)这样的分布式事务管理器,否则Spring的

@Transactional
注解是无法跨多个独立数据源实现原子性(all-or-nothing)的。在大多数场景下,我们配置多数据源是为了隔离,而不是为了在一个事务中同时操作多个数据库。如果你的业务真的需要跨多个数据源的原子性操作,那么你需要考虑引入JTA(例如使用Atomikos或Narayana),但这会大大增加系统的复杂性。对于大多数多数据源应用,独立事务管理已经足够满足需求了。

所以,我的建议是,尽量保持业务逻辑的清晰和单一职责。一个服务方法,最好只专注于操作一个数据源。如果确实需要协调多个数据源的操作,你可能需要更高层级的业务逻辑来编排这些独立事务,并在应用层面处理可能出现的补偿逻辑,而不是依赖一个“神奇”的分布式事务来解决所有问题。

以上就是在Spring Boot应用中配置多数据源(Multiple MySQL DataSources)的详细内容,更多请关注知识资源分享宝库其它相关文章!

相关标签: mysql word java app ssl ai 路由 为什么 Java mysql spring spring boot 架构 分布式 xml 堆 并发 数据库 性能优化 系统架构 大家都在看: MySQL内存使用过高(OOM)的诊断与优化配置 MySQL与NoSQL的融合:探索MySQL Document Store的应用 如何通过canal等工具实现MySQL到其他数据源的实时同步? 使用Debezium进行MySQL变更数据捕获(CDC)实战 如何设计和优化MySQL中的大表分页查询方案

标签:  配置 Boot Spring 

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。