【springboot】 多数据源实现

发布于:2024-12-06 ⋅ 阅读:(25) ⋅ 点赞:(0)

1. 引言:多数据源的必要性和应用场景

在现代软件开发中,随着业务的复杂性增加和数据量的急剧膨胀,一个应用可能需要同时访问多种数据库来满足不同的数据处理需求。例如,一个电子商务系统可能需要一个数据库来处理交易数据,另一个数据库来分析用户行为。这就是多数据源配置的必要性所在。

为什么需要多数据源?
  1. 业务逻辑分离:不同的数据库可以承载不同的业务模块,例如,订单处理和用户分析可以分别在不同的数据库中进行,使得业务更加清晰,同时提高系统的可维护性。
  2. 性能优化:通过分散请求到专门的数据库,可以避免单一数据库因请求过多而成为性能瓶颈,从而提高整个应用的性能。
  3. 数据安全和隔离:敏感数据可以单独存储在更加安全的数据库中,普通数据则可以在常规安全级别的数据库中处理,从而提高数据安全性。
  4. 技术多样性:不同的数据库技术(如关系型数据库和非关系型数据库)有其特定优势,多数据源配置允许在一个项目中利用各种数据库技术的优势。
应用场景
  1. 大数据分析与实时业务系统并行:在很多企业级应用中,通常需要将实时业务数据库和大数据分析数据库分开管理,以免复杂的数据分析操作影响到核心业务系统的性能。
  2. 微服务架构:在微服务架构中,各个微服务可能需要独立的数据库实例,以保持服务的自治性和解耦。
  3. 遗留系统整合:在新旧系统整合过程中,可能需要同时接入新旧系统的数据库,以保证数据的完整性和一致性。
  4. 跨地域数据处理:全球化业务可能需要在不同地域部署数据库,以减少数据访问延迟和符合地域数据法规。

通过这些场景,我们可以看出,多数据源的配置不仅是一种技术需求,更是业务发展和数据管理策略的一部分。

2. Spring Boot中的数据源配置

Spring Boot极大地简化了Java应用的数据库连接和操作。它通过自动配置支持快速启动和运行,但在处理多数据源时,需要进行一些额外的配置。以下是如何在Spring Boot中配置单个默认数据源以及如何扩展到多数据源的详细说明。

2.1 默认数据源配置简介

在Spring Boot中,配置一个默认的数据源非常简单。你只需要在application.propertiesapplication.yml文件中添加相关的数据库连接配置即可。Spring Boot使用这些属性自动配置数据源和JPA或JDBC模板。

示例 - application.properties配置:

spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=myuser
spring.datasource.password=mypass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

这些配置告诉Spring Boot如何连接到数据库,并使用哪个JDBC驱动。Spring Boot支持大多数主流数据库,并自动配置连接池(默认是HikariCP)。

2.2 如何在Spring Boot中配置多个数据源

配置多个数据源需要更多的手动设置。你需要定义每个数据源的配置,并确保Spring Boot能够正确地区分和管理它们。以下是配置多数据源的步骤:

步骤 1: 定义数据源配置

首先,你需要在配置文件中为每个数据源定义不同的前缀。例如:

# 主数据源
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver

# 辅助数据源
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver

步骤 2: 创建数据源配置类

你需要创建配置类来加载和区分这些数据源。每个数据源都应该有自己的配置类和@Bean定义。

@Configuration
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

在这个配置中,@Primary注解标记了主数据源,这意味着如果有多个数据源可用,Spring Boot会默认使用标记为@Primary的数据源。

步骤 3: 配置JdbcTemplate或EntityManager

对于每个数据源,你可能需要配置一个JdbcTemplateEntityManager以便于操作数据库。

@Bean
public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}

@Bean
public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}

通过以上步骤,你可以在Spring Boot应用中配置并使用多个数据源。这种配置方式不仅可以满足复杂的业务需求,还可以帮助你更好地管理和维护不同的数据环境。

3. 整合MyBatis与多数据源

MyBatis 是一个流行的持久层框架,它支持定制化 SQL、存储过程以及高级映射。在 Spring Boot 应用中整合 MyBatis 用于多数据源管理,需要一些特定的配置来确保每个数据源正确地与对应的 SQL 映射和事务管理器绑定。

配置MyBatis使用多数据源

要配置 MyBatis 使用多个数据源,你需要定义每个数据源的 SqlSessionFactoryTransactionManager。这确保了 MyBatis 能够为每个数据库连接提供独立的会话和事务控制。

  1. 定义数据源:如之前所述,定义多个数据源。
  2. 创建 SqlSessionFactory:为每个数据源创建一个 SqlSessionFactory,这是 MyBatis 的核心组件,用于管理 MyBatis 的所有 SQL 操作。
  3. 配置 TransactionManager:为每个数据源配置一个事务管理器,确保事务的正确管理。
@Configuration
public class MyBatisConfig {

    @Bean
    @Primary
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Bean
    public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Bean
    @Primary
    public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
Mapper接口的数据源指定

在定义了不同的 SqlSessionFactory 后,需要指定每个 Mapper 接口或 XML 映射文件应该使用哪个数据源。这通常通过在 Mapper 接口上使用特定的注解或通过配置来实现。

  1. 使用@MapperScan注解指定数据源:可以在配置类中使用 @MapperScan 注解为不同的包指定不同的 SqlSessionFactory
@Configuration
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
    // Primary data source configuration
}

@Configuration
@MapperScan(basePackages = "com.example.secondary.mapper", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
    // Secondary data source configuration
}

通过这种方式,你可以确保每个数据源的 Mapper 接口只会与指定的 SqlSessionFactory 交互,从而实现真正意义上的数据源隔离。这样的配置使得管理大型项目中的多数据源更加灵活和高效。

4. 数据源的动态路由

在多数据源的环境中,动态数据源路由成为一种强大的策略,允许应用程序在运行时根据特定的逻辑或条件选择不同的数据库。这对于需要根据用户请求、事务特性或其他业务逻辑动态切换数据源的应用尤其有用。

4.1 什么是数据源路由?

数据源路由是一种机制,它根据一定的规则动态地决定数据库操作应该走哪个数据源。例如,在一个多租户系统中,每个租户可能有自己的数据库,系统需要根据当前用户的租户信息动态切换到相应的数据库。

4.2 实现一个简单的动态数据源路由

为了实现动态数据源路由,我们通常需要创建一个动态数据源路由器,这个路由器继承自AbstractRoutingDataSource,并重写determineCurrentLookupKey()方法来决定使用哪个数据源键。

以下是一个简单的实现示例:

1. 定义动态数据源类

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        // DynamicDataSourceContextHolder用于持有当前线程使用的数据源标识
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

2. 配置动态数据源

在Spring配置文件中,你需要配置DynamicDataSource作为数据源,并指定实际的数据源映射。

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
        // 配置多数据源
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("primary", primaryDataSource());
        dataSourceMap.put("secondary", secondaryDataSource());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        
        return dynamicDataSource;
    }

    @Bean
    public DataSource primaryDataSource() {
        return new HikariDataSource(); // 配置主数据源
    }

    @Bean
    public DataSource secondaryDataSource() {
        return new HikariDataSource(); // 配置次级数据源
    }
}

3. 使用@Transactional指定数据源

在服务或数据访问层,你可以通过指定@Transactional注解的value属性来选择特定的数据源。

@Service
public class UserService {

    @Transactional(value = "primaryTransactionManager")
    public void addUser(User user) {
        // 使用主数据源添加用户
    }

    @Transactional(value = "secondaryTransactionManager")
    public User findUser(String username) {
        // 使用次级数据源查找用户
    }
}

这样的设置允许应用在运行时根据业务需求灵活选择合适的数据源,从而实现更复杂的数据操作策略和更高的数据操作灵活性。

5. 事务管理配置

在处理多数据源时,事务管理变得尤为重要,因为不当的事务处理可能导致数据不一致和其他严重问题。在Spring Boot应用中,正确配置和管理多数据源下的事务是确保数据完整性和一致性的关键。

如何管理多数据源下的事务
  1. 局部事务

    • 局部事务是最简单的事务类型,它仅涉及单一数据源。在Spring Boot中,你可以为每个数据源配置一个事务管理器。然后,你可以在服务层使用@Transactional注解来指定使用哪个事务管理器。
    • 示例配置事务管理器:
      @Bean
      public DataSourceTransactionManager transactionManager1(DataSource dataSource1) {
          return new DataSourceTransactionManager(dataSource1);
      }
      
      @Bean
      public DataSourceTransactionManager transactionManager2(DataSource dataSource2) {
          return new DataSourceTransactionManager(dataSource2);
      }
      
    • 使用指定的事务管理器:
      @Transactional(transactionManager = "transactionManager1")
      public void someDataServiceMethod() {
          // 业务逻辑
      }
      
  2. 全局事务

    • 全局事务(也称为分布式事务)涉及多个数据源或服务。在Spring Boot中,可以使用JTA(Java Transaction API)来配置全局事务管理器,如Atomikos或Bitronix。
    • 配置全局事务管理器(使用Atomikos为例):
      @Bean(initMethod = "init", destroyMethod = "close")
      public UserTransactionManager atomikosTransactionManager() throws Throwable {
          UserTransactionManager userTransactionManager = new UserTransactionManager();
          userTransactionManager.setForceShutdown(false);
          return userTransactionManager;
      }
      
      @Bean
      public JtaTransactionManager transactionManager(UserTransactionManager userTransactionManager) {
          JtaTransactionManager transactionManager = new JtaTransactionManager();
          transactionManager.setTransactionManager(userTransactionManager);
          transactionManager.setUserTransaction(userTransactionManager);
          return transactionManager;
      }
      
    • 在服务层使用全局事务管理器:
      @Transactional
      public void someDataServiceMethod() {
          // 业务逻辑涉及多个数据源
      }
      
配置全局事务和局部事务

在配置事务管理时,你需要决定每个业务场景适合使用局部事务还是全局事务。局部事务简单且性能较好,适用于单一数据源操作。全局事务适用于需要跨多个数据库或服务的操作,但可能会带来更高的性能开销。

确保在配置和使用事务时,明确区分不同的事务管理器,特别是在使用@Transactional注解时指定正确的管理器,以避免事务管理混乱导致的数据问题。

6. 实战示例:完整的多数据源配置示例

在这个部分,我们将通过一个实际的示例来展示如何在Spring Boot应用中配置和使用多个数据源。这个示例将包括配置文件、数据源配置、MyBatis集成、以及动态数据源的实现。

示例概述

假设我们有一个应用需要同时访问两个数据库:一个是主数据库(用于日常业务操作),另一个是审计数据库(用于记录审计日志)。我们将使用MySQL作为数据库。

步骤 1: 添加依赖

首先,在pom.xml中添加必要的依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>
步骤 2: 配置数据源

application.yml中配置两个数据源:

spring:
  datasource:
    primary:
      jdbc-url: jdbc:mysql://localhost:3306/business_db
      username: user1
      password: pass1
      driver-class-name: com.mysql.cj.jdbc.Driver
    audit:
      jdbc-url: jdbc:mysql://localhost:3306/audit_db
      username: user2
      password: pass2
      driver-class-name: com.mysql.cj.jdbc.Driver
步骤 3: 配置数据源 Bean 和 MyBatis

在Spring配置类中配置两个数据源和对应的SqlSessionFactory

@Configuration
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "auditDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.audit")
    public DataSource auditDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }

    @Bean(name = "auditSqlSessionFactory")
    public SqlSessionFactory auditSqlSessionFactory(@Qualifier("auditDataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }
}
步骤 4: 动态数据源路由

实现一个简单的动态数据源路由可以通过扩展AbstractRoutingDataSource

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContext.getCurrentDataSource();
    }
}

这里的DataSourceContext是一个管理数据源键(比如线程本地变量)的工具类,它决定了当前线程应该使用哪个数据源。

步骤 5: 配置和使用

最后,你需要在业务逻辑中根据需要切换数据源。这通常是通过在服务层方法上使用注解或直接调用DataSourceContext来设置数据源。

这个示例提供了多数据源配置的基本框架,你可以根据实际需求调整和扩展,例如添加事务管理器配置、优化连接池设置等。

7. 简化多数据源管理-dynamic

dynamic-datasource-spring-boot-starter 是一个用于 Spring Boot 应用的数据源管理框架。它不仅是一个简单的组件,而是一个完整的解决方案,旨在简化多数据源的配置和管理。这个框架提供了在运行时动态切换数据源的能力,这对于需要连接到多个数据库的应用程序来说非常有用,特别是在需要实现读写分离或连接到不同类型数据库的场景中。

功能和特点

dynamic-datasource-spring-boot-starter 的主要功能和特点包括:

  1. 简化多数据源配置:通过在 Spring Boot 应用的配置文件中声明多个数据源,使得配置变得简单直观。
  2. 动态数据源切换:提供了基于方法或类的注解(如 @DS),允许开发者轻松指定哪个数据源应该被用于特定的操作。
  3. 灵活的数据源路由:支持基于业务逻辑动态选择数据源,例如根据用户的请求参数或其他逻辑来切换数据源。
  4. 支持主从复制:非常适合实现数据库的读写分离,提高数据库操作的效率和性能。
  5. 与 Spring 生态系统的集成:与 Spring Boot 和 Spring Data JPA 等其他 Spring 组件良好集成,使得它可以无缝地与现有的 Spring 应用协同工作。
应用场景
  • 读写分离:在需要处理大量读操作和相对较少的写操作的应用中,可以将读操作指向从数据库,而写操作指向主数据库。
  • 性能和可用性:通过分散请求到多个数据源,可以提高应用的响应时间和整体性能。
  • 数据隔离:在多租户系统中,每个租户可能需要独立的数据库。使用 dynamic-datasource-spring-boot-starter 可以轻松管理每个租户的数据源。

总之,dynamic-datasource-spring-boot-starter 是一个功能强大的多数据源管理框架,非常适用于需要高度灵活性和强大数据源管理功能的复杂应用程序。它通过简化配置和增加运行时数据源切换的能力,极大地提高了开发效率和应用性能。

引入依赖

首先,确保你的 Spring Boot 项目中已经添加了 dynamic-datasource-spring-boot-starter 的依赖。如果还没有添加,可以在 pom.xml 中加入以下依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>版本</version>
</dependency>

版本根据实际项目的依赖版本选择

配置多数据源

application.ymlapplication.properties 文件中配置多个数据源。例如,使用 YAML 格式配置如下:

spring:
  datasource:
    dynamic:
      primary: master # 设置主数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/master_db
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3306/slave_db
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
使用 @DS 注解

@DS 是一个用于多数据源分离的注解,提供了在同一个应用中同时使用多个数据源的能力。这个注解属于 dynamic-datasource-spring-boot-starter 库,用于在 Spring Boot 应用中动态管理多个数据源。下面是关于如何使用 @DS 注解的一些基本信息和示例。

在方法上使用
import com.baomidou.dynamic.datasource.annotation.DS;

@Service
public class SomeService {

    @DS("master")
    public void writeToMaster() {
        // 这里的操作会使用配置中的 master 数据源
    }

    @DS("slave")
    public void readFromSlave() {
        // 这里的操作会使用配置中的 slave 数据源
    }
}
在类上使用

如果一个类中的所有方法都应该使用同一个数据源,可以将 @DS 注解放在类级别:

import com.baomidou.dynamic.datasource.annotation.DS;

@DS("slave")
@Service
public class ReadOnlyService {
    // 所有方法都将使用 slave 数据源
    public void queryData() {
        // ...
    }
}
注意事项
  • 确保数据源的键(如 masterslave)与你在配置文件中定义的名称相匹配。
  • 使用 @DS 注解时,动态数据源会在方法调用前切换到指定的数据源,并在方法执行完毕后切换回原来的数据源。
  • 考虑事务管理的策略,特别是在使用多数据源时,可能需要配置多个事务管理器。

通过这种方式,@DS 注解为处理多数据源提供了一个非常灵活和强大的机制,使得在同一个应用中根据业务需要选择不同的数据源变得简单和直接。


网站公告

今日签到

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