在 Java 框架 Spring 中,动态数据库的使用通常涉及根据不同条件(如请求的上下文、租户 ID 等)动态切换数据源。Spring 提供了一些方法来实现这一点,以下是一个常见的实现方案:

1. 基于 AbstractRoutingDataSource 实现动态数据库切换

Spring 提供了 AbstractRoutingDataSource 类,可以通过扩展该类来实现动态数据库路由。我们可以根据业务需求动态选择数据源。

步骤一:创建动态数据源路由类

首先,创建一个继承自 AbstractRoutingDataSource 的类,重写 determineCurrentLookupKey 方法。这个方法会根据当前上下文返回需要使用的数据源的标识符。

public class DynamicDataSourceRouter extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 从 ThreadLocal 中获取当前数据源标识符
        return DataSourceContextHolder.getDataSource();
    }
}

DataSourceContextHolder 是一个自定义的工具类,用于在 ThreadLocal 中保存当前数据源标识符:

public class DataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {
        CONTEXT_HOLDER.remove();
    }
}

步骤二:配置多个数据源

在 Spring 的配置类中,配置多个数据源,并将其注入到 DynamicDataSourceRouter 中。

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("dataSource1", dataSource1());
        targetDataSources.put("dataSource2", dataSource2());

        DynamicDataSourceRouter dynamicDataSourceRouter = new DynamicDataSourceRouter();
        dynamicDataSourceRouter.setDefaultTargetDataSource(dataSource1()); // 设置默认数据源
        dynamicDataSourceRouter.setTargetDataSources(targetDataSources);

        return dynamicDataSourceRouter;
    }

    @Bean
    public DataSource dataSource1() {
        // 配置数据源1
    }

    @Bean
    public DataSource dataSource2() {
        // 配置数据源2
    }
}

步骤三:在业务层切换数据源

在业务层中,可以通过调用 DataSourceContextHolder 来切换当前数据源:

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void switchDataSourceAndExecute(String dataSourceKey) {
        try {
            DataSourceContextHolder.setDataSource(dataSourceKey);
            // 执行数据库操作
            userRepository.findAll();
        } finally {
            DataSourceContextHolder.clearDataSource(); // 清理数据源,恢复默认
        }
    }
}

2. 使用 Spring Boot 多数据源配置

在 Spring Boot 项目中,还可以通过配置文件来定义多个数据源,并手动切换或使用 @Primary 注解指定默认数据源。

配置文件定义多个数据源

application.ymlapplication.properties 文件中定义多个数据源:

spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primary_db
      username: root
      password: password
    secondary:
      url: jdbc:mysql://localhost:3306/secondary_db
      username: root
      password: password

配置多个数据源 Bean

在配置类中定义多个数据源 Bean:

@Configuration
public class MultiDataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @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();
    }
}

使用不同的数据源

在 DAO 层或 Service 层中,通过 @Qualifier 注解指定使用的数据库源:

@Service
public class ProductService {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    public void someMethod() {
        // 使用 primaryDataSource 执行操作
    }

    public void anotherMethod() {
        // 使用 secondaryDataSource 执行操作
    }
}

3. 使用第三方库如 Spring Cloud 或 ShardingSphere

对于更复杂的场景,如分库分表、微服务环境中的多数据源管理,可以考虑使用 ShardingSphere、Spring Cloud 等第三方库,它们提供了更加丰富的功能支持动态数据库切换。

总结

通过以上方法,Spring 框架可以轻松实现动态数据库的管理和切换。无论是通过 AbstractRoutingDataSource 的自定义实现,还是通过 Spring Boot 的多数据源配置,都可以在不同场景下有效应用。