dynamic-datasource 多数据源

注:dynamic-datasource 不支持 JPA

1.项目结构

framework-dynamic-datasource
└─src
    └─main
        ├─java
        │  └─com
        │      └─yan
        │          ├─abstractinterface
        │          │  └─config
        │          │      └─dynamic
        │          │          └─Impl
        │          └─config
        │              └─dynamic
        └─resources

2.com.yan.abstractinterface.config.dynamic

2.1 AbstractDynamicDataSource

DynamicDataSource 抽象接口可通过实现该接口 的 getDataSourceName() 指定数据源名称

已支持shardingjdbc原生的配置方式

package com.yan.abstractinterface.config.dynamic;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.util.Map;
import java.util.logging.Logger;

/**
 * @Author yan
 * @Date 2024/10/25 上午8:05:07
 * @Description
 */
public interface AbstractDynamicDataSource {
    /**
     * 默认分表数据源名称
     */
    String SHARDING_DATA_SOURCE_NAME = "sharding";

    /**
     * 分表数据源名称 重写修改数据源名称
     *
     * @return
     */
    default String getDataSourceName() {
        return SHARDING_DATA_SOURCE_NAME;
    }

    /**
     * 将动态数据源设置为首选的
     * 当spring存在多个数据源时, 自动注入的是首选的对象
     * 设置为主要的数据源之后,就可以支持shardingjdbc原生的配置方式了
     *
     * @return
     */
    default DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) throws Exception {

        DynamicDataSourceProperties dynamicDataSourceProperties = getDynamicDataSourceProperties();
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(dynamicDataSourceProperties.getPrimary());
        dataSource.setStrict(dynamicDataSourceProperties.getStrict());
        dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
        dataSource.setProvider(dynamicDataSourceProvider);
        dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
        dataSource.setSeata(dynamicDataSourceProperties.getSeata());
        return dataSource;
    }

    default DataSource dataSource() throws Exception {
        DynamicDataSourceProvider dynamicDataSourceProvider = SpringUtil.getBean(DynamicDataSourceProvider.class);
        if (dynamicDataSourceProvider == null) {
            throw new Exception(" 分库分表数据源未配置 ");
        }
        return dataSource(dynamicDataSourceProvider);
    }

    /**
     * 动态 数据源配置
     * 将shardingDataSource放到了多数据源(dataSourceMap)中
     * 注意有个版本的bug,3.1.1版本 不会进入loadDataSources 方法,这样就一直造成数据源注册失败
     */
    default DynamicDataSourceProvider dynamicDataSourceProvider() throws Exception {
        Map<String, DataSourceProperty> datasourceMap = getDynamicDataSourceProperties().getDatasource();
        return new AbstractDataSourceProvider() {
            @Override
            public Map<String, DataSource> loadDataSources() {
                Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap);
                // 将 shardingjdbc 管理的数据源也交给动态数据源管理
                try {
                    org.slf4j.Logger logger = LoggerFactory.getLogger(getClass().getName());
                    String dataSourceName = getDataSourceName();
                    DataSource dataSource = getDataSource();
                    if (ObjectUtil.isNotEmpty(dataSource) && ObjectUtil.isNotEmpty(dataSourceName)) {
                        logger.info("dataSourceName : {}", dataSourceName);
                        dataSourceMap.put(dataSourceName, dataSource);
                        logger.info("DataSource init success");
                    } else {
                        logger.warn("DataSource init error :name can not null and DataSource can not null");
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                Map<String, DataSource> extendedDataSource = getExtendedDataSource();
                if (CollUtil.isNotEmpty(extendedDataSource)) {
                    dataSourceMap.putAll(extendedDataSource);
                }
                return dataSourceMap;
            }
        };
    }

    /**
     * 扩展数据源
     *
     * @return
     */
    default Map<String, DataSource> getExtendedDataSource() {
        return null;
    }

    /**
     * 分库分表数据源
     *
     * @return
     * @throws Exception
     */
    default DataSource getDataSource() throws Exception {
        return SpringUtil.getBean(DataSource.class);
    }

    default DynamicDataSourceProperties getDynamicDataSourceProperties() throws Exception {
        DynamicDataSourceProperties dynamicDataSourceProperties = SpringUtil.getBean(DynamicDataSourceProperties.class);
        if (dynamicDataSourceProperties == null) {
            throw new Exception(" 多数据源未配置 ");
        }
        return dynamicDataSourceProperties;
    }
}

2.2 DefaultDynamicDataSourceImpl

默认实现方式 DefaultDynamicDataSourceImpl 使用 @Service @Primary 注解覆盖 DefaultDynamicDataSourceImpl

package com.yan.abstractinterface.config.dynamic.Impl;

import com.yan.abstractinterface.config.dynamic.AbstractDynamicDataSource;
import org.springframework.stereotype.Service;

/**
 * @Author yan
 * @Date 2024/10/25 上午9:00:36
 * @Description
 */
@Service
public class DefaultDynamicDataSourceImpl implements AbstractDynamicDataSource {
}

3.com.yan.config.dynamic

3.1 DynamicDataSourceConfig

实际配置注入

package com.yan.config.dynamic;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.yan.abstractinterface.config.dynamic.AbstractDynamicDataSource;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @Author yan
 * @Date 2024/7/3 0003 15:59:02
 * @Description 不使用该配置类,使用以下注释在启动类上排除
 * @ComponentScan(excludeFilters = {
 * @ComponentScan.Filter(type = FilterType.REGEX,pattern = "com.yan.config.dynamic.DynamicDataSourceConfig")
 * })
 */

@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DynamicDataSourceConfig {
    private AbstractDynamicDataSource getAbstractDynamicDataSource() {
        return SpringUtil.getBean(AbstractDynamicDataSource.class);
    }


    public DataSource getDataSource() throws Exception {
        return getAbstractDynamicDataSource().getDataSource();
    }

    /**
     * 将shardingDataSource放到了多数据源(dataSourceMap)中
     * 注意有个版本的bug,3.1.1版本 不会进入loadDataSources 方法,这样就一直造成数据源注册失败
     */

    @Bean
    public DynamicDataSourceProvider initDynamicDataSourceProvider() throws Exception {
        return getAbstractDynamicDataSource().dynamicDataSourceProvider();
    }

    /**
     * 将动态数据源设置为首选的
     * 当spring存在多个数据源时, 自动注入的是首选的对象
     * 设置为主要的数据源之后,就可以支持shardingjdbc原生的配置方式了
     * 以下二选一为bean Primary
     *
     * @return
     */

    @Primary
    @Bean
    public DataSource initDataSource() throws Exception {
        return getAbstractDynamicDataSource().dataSource();
    }

    //initDataSource()重复  选1个即可
    //@Primary
    //@Bean
    public DataSource initDataSource(DynamicDataSourceProvider dynamicDataSourceProvider) throws Exception {
        return getAbstractDynamicDataSource().dataSource(dynamicDataSourceProvider);
    }
}

4.依赖

        <dependency>
            <groupId>com.yan</groupId>
            <artifactId>framework-dynamic-datasource</artifactId>
            <version>${project-module.version}</version>
        </dependency>