Spring Boot 多数据源配置

发布于:2025-09-09 ⋅ 阅读:(29) ⋅ 点赞:(0)

一、为什么需要多数据源?
多数据源在以下场景中非常有用:

数据库分库分表:业务数据分布在不同的数据库实例中

读写分离:主库处理写操作,从库处理读操作

多租户系统:每个租户使用独立的数据源

异构数据库:同时使用 MySQL、PostgreSQL、Oracle 等不同数据库

数据迁移和同步:需要同时访问新旧两个数据库系统

二、基础依赖配置
首先在 pom.xml 中添加必要的依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- 数据访问相关 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- 多数据源动态路由 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>
    
    <!-- 数据库驱动 -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

三、方案一:基于 dynamic-datasource 的配置(推荐)
3.1 配置文件设置

spring:
  datasource:
    dynamic:
      primary: master    # 设置默认数据源
      strict: true       # 严格匹配模式
      datasource:
        master:
          url: jdbc:postgresql://localhost:5432/main_db
          username: admin
          password: password
          driver-class-name: org.postgresql.Driver
        slave:
          url: jdbc:mysql://localhost:3306/log_db
          username: log_user
          password: log_password
          driver-class-name: com.mysql.cj.jdbc.Driver
        report:
          url: jdbc:postgresql://localhost:5432/report_db
          username: report_user
          password: report_password
          driver-class-name: org.postgresql.Driver
      
      # 连接池配置
      hikari:
        max-pool-size: 20
        min-idle: 5
        connection-timeout: 30000
        idle-timeout: 600000

3.2 使用注解切换数据源

@Service
public class BusinessService {
    
    // 使用默认数据源(master)
    public void createUser(User user) {
        userRepository.save(user);
    }
    
    // 使用从数据源
    @DS("slave")
    public List<Log> getLogs(String userId) {
        return logRepository.findByUserId(userId);
    }
    
    // 使用报表数据源
    @DS("report")
    public Report generateReport(Date startDate, Date endDate) {
        return reportRepository.generateReport(startDate, endDate);
    }
}

3.3 事务管理

@Service
public class TransactionalService {
    
    // 单个数据源事务
    @Transactional
    @DS("master")
    public void updateUser(User user) {
        userRepository.save(user);
    }
    
    // 多数据源事务(需要分布式事务支持)
    @DS("master")
    @Transactional
    public void crossDatabaseOperation(User user, Log log) {
        userRepository.save(user);
        switchToSlave();
        logRepository.save(log);
    }
    
    @DS("slave")
    public void switchToSlave() {
        // 方法级别切换数据源
    }
}

四、方案二:手动配置多数据源

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = "com.example.repository.primary",
    entityManagerFactoryRef = "primaryEntityManagerFactory",
    transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {
    
    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(primaryDataSource())
                .packages("com.example.entity.primary")
                .persistenceUnit("primary")
                .properties(jpaProperties())
                .build();
    }
    
    @Bean
    @Primary
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
    
    private Map<String, Object> jpaProperties() {
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.hbm2ddl.auto", "update");
        props.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        return props;
    }
}

4.2 从数据源配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = "com.example.repository.secondary",
    entityManagerFactoryRef = "secondaryEntityManagerFactory",
    transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(secondaryDataSource())
                .packages("com.example.entity.secondary")
                .persistenceUnit("secondary")
                .properties(jpaProperties())
                .build();
    }
    
    @Bean
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

五、方案三:动态数据源路由
5.1 数据源路由配置

public class DynamicDataSource extends AbstractRoutingDataSource {
    
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
    public static void setDataSourceKey(String dataSourceKey) {
        CONTEXT_HOLDER.set(dataSourceKey);
    }
    
    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }
    
    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }
    
    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSourceKey();
    }
}

@Configuration
public class DynamicDataSourceConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());
        
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
        
        return dynamicDataSource;
    }
}

5.2 切面编程实现自动切换

@Aspect
@Component
public class DataSourceAspect {
    
    @Before("@annotation(targetDataSource)")
    public void switchDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDataSource.value())) {
            throw new RuntimeException("数据源 " + targetDataSource.value() + " 不存在");
        }
        DynamicDataSourceContextHolder.setDataSourceKey(targetDataSource.value());
    }
    
    @After("@annotation(targetDataSource)")
    public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
        DynamicDataSourceContextHolder.clearDataSourceKey();
    }
}

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String value() default "master";
}

网站公告

今日签到

点亮在社区的每一天
去签到