👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高的教辅资料推荐等,欢迎关注交流!🚀

多数据源配置管理

前言

在现代企业级应用中,多数据源已成为常见需求。RuoYi-Vue-Plus框架基于Dynamic-Datasource实现了强大的多数据源管理功能,支持主从分离、读写分离、多租户数据隔离等场景。本文将详细介绍Dynamic-Datasource的集成配置、多数据源动态切换、数据源路由策略以及异构数据库支持等内容。

Dynamic-Datasource集成

基础配置

/**
 * 多数据源配置
 */
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceConfig {
    
    @Autowired
    private DynamicDataSourceProperties properties;
    
    /**
     * 数据源配置
     */
    @Bean
    @ConfigurationProperties("spring.datasource.dynamic")
    public DynamicDataSourceProperties dynamicDataSourceProperties() {
        return new DynamicDataSourceProperties();
    }
    
    /**
     * 动态数据源
     */
    @Bean
    @Primary
    public DataSource dynamicDataSource() {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        
        // 设置默认数据源
        dataSource.setPrimary(properties.getPrimary());
        
        // 设置严格模式
        dataSource.setStrict(properties.getStrict());
        
        // 设置数据源
        Map<String, DataSourceProperty> datasourceMap = properties.getDatasource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        
        for (Map.Entry<String, DataSourceProperty> entry : datasourceMap.entrySet()) {
            String key = entry.getKey();
            DataSourceProperty property = entry.getValue();
            
            DataSource ds = createDataSource(property);
            targetDataSources.put(key, ds);
        }
        
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(targetDataSources.get(properties.getPrimary()));
        
        return dataSource;
    }
    
    /**
     * 创建数据源
     */
    private DataSource createDataSource(DataSourceProperty property) {
        HikariConfig config = new HikariConfig();
        
        // 基础配置
        config.setJdbcUrl(property.getUrl());
        config.setUsername(property.getUsername());
        config.setPassword(property.getPassword());
        config.setDriverClassName(property.getDriverClassName());
        
        // 连接池配置
        config.setMaximumPoolSize(property.getHikari().getMaximumPoolSize());
        config.setMinimumIdle(property.getHikari().getMinimumIdle());
        config.setConnectionTimeout(property.getHikari().getConnectionTimeout());
        config.setIdleTimeout(property.getHikari().getIdleTimeout());
        config.setMaxLifetime(property.getHikari().getMaxLifetime());
        config.setValidationTimeout(property.getHikari().getValidationTimeout());
        
        // 连接测试
        config.setConnectionTestQuery(property.getHikari().getConnectionTestQuery());
        
        // 连接池名称
        config.setPoolName(property.getPoolName());
        
        return new HikariDataSource(config);
    }
    
    /**
     * 数据源切换处理器
     */
    @Bean
    public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor() {
        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
        DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
        advisor.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return advisor;
    }
    
    /**
     * 数据源健康检查
     */
    @Bean
    public DataSourceHealthIndicator dataSourceHealthIndicator() {
        return new DataSourceHealthIndicator();
    }
}

配置文件

spring:
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      # 严格模式,默认false. 设置为true后在未匹配到指定数据源时候会抛出异常
      strict: false
      # 数据源配置
      datasource:
        # 主数据源
        master:
          url: jdbc:mysql://localhost:3306/ry-vue-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
          # 连接池配置
          hikari:
            maximum-pool-size: 20
            minimum-idle: 5
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
            validation-timeout: 5000
            connection-test-query: SELECT 1
          
        # 从数据源
        slave:
          url: jdbc:mysql://localhost:3307/ry-vue-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
          hikari:
            maximum-pool-size: 15
            minimum-idle: 3
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
            validation-timeout: 5000
            connection-test-query: SELECT 1
            
        # Oracle数据源
        oracle:
          url: jdbc:oracle:thin:@localhost:1521:orcl
          username: ruoyi
          password: password
          driver-class-name: oracle.jdbc.OracleDriver
          hikari:
            maximum-pool-size: 10
            minimum-idle: 2
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
            validation-timeout: 5000
            connection-test-query: SELECT 1 FROM DUAL
            
        # PostgreSQL数据源
        postgres:
          url: jdbc:postgresql://localhost:5432/ruoyi
          username: postgres
          password: password
          driver-class-name: org.postgresql.Driver
          hikari:
            maximum-pool-size: 10
            minimum-idle: 2
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
            validation-timeout: 5000
            connection-test-query: SELECT 1
            
        # 租户数据源示例
        tenant1:
          url: jdbc:mysql://localhost:3306/tenant1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
          username: tenant1
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
          hikari:
            maximum-pool-size: 10
            minimum-idle: 2
            connection-timeout: 30000
            idle-timeout: 600000
            max-lifetime: 1800000
            validation-timeout: 5000
            connection-test-query: SELECT 1

多数据源动态切换

注解方式切换

/**
 * 用户服务 - 演示多数据源切换
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    
    /**
     * 查询用户列表 - 使用从库
     */
    @DS("slave")
    public List<SysUserVo> selectUserList(SysUserBo user) {
        LambdaQueryWrapper<SysUser> wrapper = buildQueryWrapper(user);
        List<SysUser> userList = baseMapper.selectList(wrapper);
        return BeanUtil.copyToList(userList, SysUserVo.class);
    }
    
    /**
     * 新增用户 - 使用主库
     */
    @DS("master")
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertUser(SysUserBo user) {
        SysUser sysUser = BeanUtil.toBean(user, SysUser.class);
        
        // 新增用户信息
        int rows = baseMapper.insert(sysUser);
        user.setUserId(sysUser.getUserId());
        
        // 新增用户岗位关联
        insertUserPost(user);
        
        // 新增用户与角色管理
        insertUserRole(user);
        
        return rows > 0;
    }
    
    /**
     * 从Oracle数据源查询用户统计信息
     */
    @DS("oracle")
    public UserStatisticsVo getUserStatisticsFromOracle() {
        // 这里可以调用Oracle特有的统计函数
        return baseMapper.selectUserStatisticsFromOracle();
    }
    
    /**
     * 从PostgreSQL数据源查询用户分析数据
     */
    @DS("postgres")
    public List<UserAnalysisVo> getUserAnalysisFromPostgres(AnalysisQuery query) {
        return baseMapper.selectUserAnalysisFromPostgres(query);
    }
    
    /**
     * 租户数据查询
     */
    @DS("#tenantId")
    public List<SysUserVo> selectTenantUsers(String tenantId, SysUserBo user) {
        LambdaQueryWrapper<SysUser> wrapper = buildQueryWrapper(user);
        List<SysUser> userList = baseMapper.selectList(wrapper);
        return BeanUtil.copyToList(userList, SysUserVo.class);
    }
    
    /**
     * 动态数据源查询
     */
    @DS("#dataSourceName")
    public List<SysUserVo> selectUsersFromDynamicSource(String dataSourceName, SysUserBo user) {
        LambdaQueryWrapper<SysUser> wrapper = buildQueryWrapper(user);
        List<SysUser> userList = baseMapper.selectList(wrapper);
        return BeanUtil.copyToList(userList, SysUserVo.class);
    }
}

编程方式切换

/**
 * 数据源切换工具类
 */
@Component
public class DataSourceSwitcher {
    
    /**
     * 切换到指定数据源执行操作
     */
    public <T> T executeWithDataSource(String dataSourceName, Supplier<T> supplier) {
        String originalDataSource = DynamicDataSourceContextHolder.peek();
        try {
            DynamicDataSourceContextHolder.push(dataSourceName);
            return supplier.get();
        } finally {
            DynamicDataSourceContextHolder.poll();
            if (originalDataSource != null) {
                DynamicDataSourceContextHolder.push(originalDataSource);
            }
        }
    }
    
    /**
     * 切换到指定数据源执行操作(无返回值)
     */
    public void executeWithDataSource(String dataSourceName, Runnable runnable) {
        executeWithDataSource(dataSourceName, () -> {
            runnable.run();
            return null;
        });
    }
    
    /**
     * 在主库执行操作
     */
    public <T> T executeOnMaster(Supplier<T> supplier) {
        return executeWithDataSource("master", supplier);
    }
    
    /**
     * 在从库执行操作
     */
    public <T> T executeOnSlave(Supplier<T> supplier) {
        return executeWithDataSource("slave", supplier);
    }
    
    /**
     * 在指定租户数据源执行操作
     */
    public <T> T executeOnTenant(String tenantId, Supplier<T> supplier) {
        return executeWithDataSource("tenant_" + tenantId, supplier);
    }
}

/**
 * 使用编程方式切换数据源的服务示例
 */
@Service
public class DataMigrationService {
    
    @Autowired
    private DataSourceSwitcher dataSourceSwitcher;
    
    @Autowired
    private SysUserMapper userMapper;
    
    /**
     * 数据迁移示例
     */
    @Transactional(rollbackFor = Exception.class)
    public void migrateUserData(String sourceDataSource, String targetDataSource) {
        // 从源数据源查询数据
        List<SysUser> users = dataSourceSwitcher.executeWithDataSource(sourceDataSource, () -> {
            return userMapper.selectList(new LambdaQueryWrapper<>());
        });
        
        log.info("从数据源 {} 查询到 {} 条用户数据", sourceDataSource, users.size());
        
        // 写入目标数据源
        dataSourceSwitcher.executeWithDataSource(targetDataSource, () -> {
            for (SysUser user : users) {
                // 重置ID,避免主键冲突
                user.setUserId(null);
                user.setCreateTime(new Date());
                userMapper.insert(user);
            }
            return null;
        });
        
        log.info("成功迁移 {} 条用户数据到数据源 {}", users.size(), targetDataSource);
    }
    
    /**
     * 跨数据源数据同步
     */
    public void syncUserData(Long userId) {
        // 从主库获取最新数据
        SysUser masterUser = dataSourceSwitcher.executeOnMaster(() -> {
            return userMapper.selectById(userId);
        });
        
        if (masterUser != null) {
            // 同步到从库
            dataSourceSwitcher.executeOnSlave(() -> {
                SysUser existingUser = userMapper.selectById(userId);
                if (existingUser != null) {
                    // 更新
                    userMapper.updateById(masterUser);
                } else {
                    // 插入
                    userMapper.insert(masterUser);
                }
                return null;
            });
            
            log.info("用户数据同步完成: userId={}", userId);
        }
    }
    
    /**
     * 多数据源数据汇总
     */
    public UserSummaryVo getUserSummary(Long userId) {
        UserSummaryVo summary = new UserSummaryVo();
        
        // 从主库获取基本信息
        SysUser basicInfo = dataSourceSwitcher.executeOnMaster(() -> {
            return userMapper.selectById(userId);
        });
        summary.setBasicInfo(basicInfo);
        
        // 从Oracle获取统计信息
        UserStatisticsVo statistics = dataSourceSwitcher.executeWithDataSource("oracle", () -> {
            return userMapper.selectUserStatistics(userId);
        });
        summary.setStatistics(statistics);
        
        // 从PostgreSQL获取分析数据
        List<UserAnalysisVo> analysis = dataSourceSwitcher.executeWithDataSource("postgres", () -> {
            return userMapper.selectUserAnalysis(userId);
        });
        summary.setAnalysis(analysis);
        
        return summary;
    }
}

数据源路由策略

读写分离策略

/**
 * 读写分离数据源路由策略
 */
@Component
public class ReadWriteSplitStrategy implements DynamicDataSourceStrategy {
    
    private static final Logger log = LoggerFactory.getLogger(ReadWriteSplitStrategy.class);
    
    /**
     * 确定数据源
     */
    @Override
    public String determineDataSource(String[] dataSourceNames) {
        // 获取当前事务状态
        boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
        
        // 检查是否在事务中
        boolean isInTransaction = TransactionSynchronizationManager.isActualTransactionActive();
        
        String selectedDataSource;
        
        if (isInTransaction && !isReadOnly) {
            // 写事务,使用主库
            selectedDataSource = selectMasterDataSource(dataSourceNames);
        } else {
            // 读操作或只读事务,使用从库
            selectedDataSource = selectSlaveDataSource(dataSourceNames);
        }
        
        log.debug("数据源路由: isReadOnly={}, isInTransaction={}, selected={}", 
                 isReadOnly, isInTransaction, selectedDataSource);
        
        return selectedDataSource;
    }
    
    /**
     * 选择主库
     */
    private String selectMasterDataSource(String[] dataSourceNames) {
        for (String dataSourceName : dataSourceNames) {
            if (dataSourceName.contains("master") || dataSourceName.contains("write")) {
                return dataSourceName;
            }
        }
        return dataSourceNames[0]; // 默认返回第一个
    }
    
    /**
     * 选择从库
     */
    private String selectSlaveDataSource(String[] dataSourceNames) {
        List<String> slaveDataSources = new ArrayList<>();
        
        for (String dataSourceName : dataSourceNames) {
            if (dataSourceName.contains("slave") || dataSourceName.contains("read")) {
                slaveDataSources.add(dataSourceName);
            }
        }
        
        if (slaveDataSources.isEmpty()) {
            // 没有从库,使用主库
            return selectMasterDataSource(dataSourceNames);
        }
        
        // 负载均衡选择从库
        return loadBalanceSelect(slaveDataSources);
    }
    
    /**
     * 负载均衡选择数据源
     */
    private String loadBalanceSelect(List<String> dataSources) {
        if (dataSources.size() == 1) {
            return dataSources.get(0);
        }
        
        // 简单的轮询策略
        int index = (int) (System.currentTimeMillis() % dataSources.size());
        return dataSources.get(index);
    }
}

租户路由策略

/**
 * 租户数据源路由策略
 */
@Component
public class TenantDataSourceStrategy implements DynamicDataSourceStrategy {
    
    @Autowired
    private TenantService tenantService;
    
    @Override
    public String determineDataSource(String[] dataSourceNames) {
        // 获取当前租户ID
        String tenantId = TenantContextHolder.getTenantId();
        
        if (StringUtils.isBlank(tenantId)) {
            log.warn("租户ID为空,使用默认数据源");
            return "master";
        }
        
        // 获取租户配置
        TenantConfig tenantConfig = tenantService.getTenantConfig(tenantId);
        if (tenantConfig == null) {
            log.warn("租户配置不存在: tenantId={}", tenantId);
            return "master";
        }
        
        // 根据租户配置选择数据源
        String dataSourceName = tenantConfig.getDataSourceName();
        
        // 验证数据源是否存在
        if (isDataSourceExists(dataSourceName)) {
            log.debug("租户数据源路由: tenantId={}, dataSource={}", tenantId, dataSourceName);
            return dataSourceName;
        } else {
            log.warn("租户数据源不存在: tenantId={}, dataSource={}", tenantId, dataSourceName);
            return "master";
        }
    }
    
    /**
     * 检查数据源是否存在
     */
    private boolean isDataSourceExists(String dataSourceName) {
        DynamicRoutingDataSource dynamicDataSource = 
            (DynamicRoutingDataSource) SpringUtils.getBean("dynamicDataSource");
        return dynamicDataSource.getDataSources().containsKey(dataSourceName);
    }
}

地域路由策略

/**
 * 地域数据源路由策略
 */
@Component
public class RegionDataSourceStrategy implements DynamicDataSourceStrategy {
    
    @Override
    public String determineDataSource(String[] dataSourceNames) {
        // 获取用户地域信息
        String region = getCurrentUserRegion();
        
        if (StringUtils.isBlank(region)) {
            return "master"; // 默认数据源
        }
        
        // 根据地域选择数据源
        String targetDataSource = "region_" + region.toLowerCase();
        
        // 检查目标数据源是否存在
        for (String dataSourceName : dataSourceNames) {
            if (dataSourceName.equals(targetDataSource)) {
                log.debug("地域数据源路由: region={}, dataSource={}", region, targetDataSource);
                return targetDataSource;
            }
        }
        
        log.debug("地域数据源不存在,使用默认数据源: region={}", region);
        return "master";
    }
    
    /**
     * 获取当前用户地域
     */
    private String getCurrentUserRegion() {
        try {
            // 从用户信息中获取地域
            if (StpUtil.isLogin()) {
                Long userId = StpUtil.getLoginIdAsLong();
                SysUser user = userService.selectUserById(userId);
                return user != null ? user.getRegion() : null;
            }
        } catch (Exception e) {
            log.debug("获取用户地域信息失败", e);
        }
        
        // 从请求头获取地域信息
        HttpServletRequest request = ServletUtils.getRequest();
        if (request != null) {
            return request.getHeader("X-User-Region");
        }
        
        return null;
    }
}

异构数据库支持

数据库适配器

/**
 * 数据库适配器接口
 */
public interface DatabaseAdapter {
    
    /**
     * 获取数据库类型
     */
    DatabaseType getDatabaseType();
    
    /**
     * 获取分页SQL
     */
    String getPageSql(String originalSql, long offset, long limit);
    
    /**
     * 获取当前时间函数
     */
    String getCurrentTimeFunction();
    
    /**
     * 获取字符串连接函数
     */
    String getConcatFunction(String... columns);
    
    /**
     * 转换数据类型
     */
    String convertDataType(String genericType);
    
    /**
     * 获取序列下一个值(用于主键生成)
     */
    String getNextSequenceValue(String sequenceName);
}

/**
 * MySQL数据库适配器
 */
@Component
public class MySQLAdapter implements DatabaseAdapter {
    
    @Override
    public DatabaseType getDatabaseType() {
        return DatabaseType.MYSQL;
    }
    
    @Override
    public String getPageSql(String originalSql, long offset, long limit) {
        return originalSql + " LIMIT " + offset + ", " + limit;
    }
    
    @Override
    public String getCurrentTimeFunction() {
        return "NOW()";
    }
    
    @Override
    public String getConcatFunction(String... columns) {
        return "CONCAT(" + String.join(", ", columns) + ")";
    }
    
    @Override
    public String convertDataType(String genericType) {
        switch (genericType.toUpperCase()) {
            case "STRING":
                return "VARCHAR(255)";
            case "TEXT":
                return "TEXT";
            case "INTEGER":
                return "INT";
            case "LONG":
                return "BIGINT";
            case "DECIMAL":
                return "DECIMAL(10,2)";
            case "DATETIME":
                return "DATETIME";
            case "BOOLEAN":
                return "TINYINT(1)";
            default:
                return "VARCHAR(255)";
        }
    }
    
    @Override
    public String getNextSequenceValue(String sequenceName) {
        // MySQL使用AUTO_INCREMENT,不需要序列
        return null;
    }
}

/**
 * Oracle数据库适配器
 */
@Component
public class OracleAdapter implements DatabaseAdapter {
    
    @Override
    public DatabaseType getDatabaseType() {
        return DatabaseType.ORACLE;
    }
    
    @Override
    public String getPageSql(String originalSql, long offset, long limit) {
        return "SELECT * FROM (SELECT ROWNUM rn, t.* FROM (" + originalSql + ") t WHERE ROWNUM <= " + 
               (offset + limit) + ") WHERE rn > " + offset;
    }
    
    @Override
    public String getCurrentTimeFunction() {
        return "SYSDATE";
    }
    
    @Override
    public String getConcatFunction(String... columns) {
        return String.join(" || ", columns);
    }
    
    @Override
    public String convertDataType(String genericType) {
        switch (genericType.toUpperCase()) {
            case "STRING":
                return "VARCHAR2(255)";
            case "TEXT":
                return "CLOB";
            case "INTEGER":
                return "NUMBER(10)";
            case "LONG":
                return "NUMBER(19)";
            case "DECIMAL":
                return "NUMBER(10,2)";
            case "DATETIME":
                return "DATE";
            case "BOOLEAN":
                return "NUMBER(1)";
            default:
                return "VARCHAR2(255)";
        }
    }
    
    @Override
    public String getNextSequenceValue(String sequenceName) {
        return sequenceName + ".NEXTVAL";
    }
}

/**
 * PostgreSQL数据库适配器
 */
@Component
public class PostgreSQLAdapter implements DatabaseAdapter {
    
    @Override
    public DatabaseType getDatabaseType() {
        return DatabaseType.POSTGRESQL;
    }
    
    @Override
    public String getPageSql(String originalSql, long offset, long limit) {
        return originalSql + " LIMIT " + limit + " OFFSET " + offset;
    }
    
    @Override
    public String getCurrentTimeFunction() {
        return "NOW()";
    }
    
    @Override
    public String getConcatFunction(String... columns) {
        return String.join(" || ", columns);
    }
    
    @Override
    public String convertDataType(String genericType) {
        switch (genericType.toUpperCase()) {
            case "STRING":
                return "VARCHAR(255)";
            case "TEXT":
                return "TEXT";
            case "INTEGER":
                return "INTEGER";
            case "LONG":
                return "BIGINT";
            case "DECIMAL":
                return "DECIMAL(10,2)";
            case "DATETIME":
                return "TIMESTAMP";
            case "BOOLEAN":
                return "BOOLEAN";
            default:
                return "VARCHAR(255)";
        }
    }
    
    @Override
    public String getNextSequenceValue(String sequenceName) {
        return "NEXTVAL('" + sequenceName + "')";
    }
}

数据库适配器工厂

/**
 * 数据库适配器工厂
 */
@Component
public class DatabaseAdapterFactory {
    
    private final Map<DatabaseType, DatabaseAdapter> adapters = new HashMap<>();
    
    @Autowired
    public DatabaseAdapterFactory(List<DatabaseAdapter> adapterList) {
        for (DatabaseAdapter adapter : adapterList) {
            adapters.put(adapter.getDatabaseType(), adapter);
        }
    }
    
    /**
     * 根据数据源名称获取适配器
     */
    public DatabaseAdapter getAdapter(String dataSourceName) {
        DatabaseType dbType = getDatabaseType(dataSourceName);
        DatabaseAdapter adapter = adapters.get(dbType);
        
        if (adapter == null) {
            throw new ServiceException("不支持的数据库类型: " + dbType);
        }
        
        return adapter;
    }
    
    /**
     * 根据当前数据源获取适配器
     */
    public DatabaseAdapter getCurrentAdapter() {
        String currentDataSource = DynamicDataSourceContextHolder.peek();
        return getAdapter(currentDataSource != null ? currentDataSource : "master");
    }
    
    /**
     * 获取数据库类型
     */
    private DatabaseType getDatabaseType(String dataSourceName) {
        try {
            DynamicRoutingDataSource dynamicDataSource = 
                (DynamicRoutingDataSource) SpringUtils.getBean("dynamicDataSource");
            
            DataSource dataSource = dynamicDataSource.getDataSource(dataSourceName);
            
            try (Connection connection = dataSource.getConnection()) {
                String databaseProductName = connection.getMetaData().getDatabaseProductName().toLowerCase();
                
                if (databaseProductName.contains("mysql")) {
                    return DatabaseType.MYSQL;
                } else if (databaseProductName.contains("oracle")) {
                    return DatabaseType.ORACLE;
                } else if (databaseProductName.contains("postgresql")) {
                    return DatabaseType.POSTGRESQL;
                } else if (databaseProductName.contains("sql server")) {
                    return DatabaseType.SQLSERVER;
                } else {
                    return DatabaseType.UNKNOWN;
                }
            }
        } catch (Exception e) {
            log.error("获取数据库类型失败: dataSource={}", dataSourceName, e);
            return DatabaseType.UNKNOWN;
        }
    }
}

/**
 * 数据库类型枚举
 */
public enum DatabaseType {
    MYSQL,
    ORACLE,
    POSTGRESQL,
    SQLSERVER,
    UNKNOWN
}

总结

本文详细介绍了多数据源配置管理,包括:

  1. Dynamic-Datasource集成:基础配置、连接池配置、健康检查
  2. 多数据源动态切换:注解方式和编程方式的数据源切换
  3. 数据源路由策略:读写分离、租户路由、地域路由等策略
  4. 异构数据库支持:数据库适配器模式,支持MySQL、Oracle、PostgreSQL等

这些功能为企业级应用提供了强大的多数据源管理能力,满足各种复杂的业务场景需求。

在下一篇文章中,我们将探讨数据库连接与事务管理。

参考资料

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐