Springboot-配置多數據源-接口切換


Application添加注解: @Import({DynamicDataSourceRegister.class}) 

application.xml配置從數據源:

slave:
    datasource:
        names: name1 ,name2
        name1:
            driver-class-name: com.mysql.jdbc.Driver
            names: datasource1
            url:
            username:
            password:
            type: com.alibaba.druid.pool.DruidDataSource
        name2:
            driver-class-name: com.mysql.jdbc.Driver
            names: datasource2
            url: 
            username: 
            password:
            type: com.alibaba.druid.pool.DruidDataSource
數據源注冊 DynamicDataSourceRegister.java
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private org.slf4j.Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
    public static  Environment environment;
    //指定默認數據源(springboot2.0默認數據源是hikari如何想使用其他數據源可以自己配置)
    private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
    //默認數據源
    private DataSource defaultDataSource;
    //用戶自定義數據源
    private Map<String, DataSource> slaveDataSources = new HashMap<>();

    @Override
    public void setEnvironment(Environment environment) {
        initDefaultDataSource(environment);
        initSlaveDataSources(environment);
        this.environment = environment;
    }

    private void initDefaultDataSource(Environment env) {
        // 讀取主數據源
        Map<String, Object> dsMap = new HashMap<>();
        dsMap.put("driver", env.getProperty("spring.datasource.driver-class-name"));
        dsMap.put("url", env.getProperty("spring.datasource.url"));
        dsMap.put("username", env.getProperty("spring.datasource.username"));
        dsMap.put("password", env.getProperty("spring.datasource.password"));
        defaultDataSource = buildDataSource(dsMap);
    }


    private void initSlaveDataSources(Environment env) {
        // 讀取配置文件獲取更多數據源
        String dsPrefixs = env.getProperty("slave.datasource.names");
        if(StringUtil.isNotEmpty(dsPrefixs)) {
            for (String dsPrefix : dsPrefixs.split(",")) {
                // 多個數據源
                Map<String, Object> dsMap = new HashMap<>();
                dsMap.put("driver", env.getProperty("slave.datasource." + dsPrefix + ".driver-class-name"));
                dsMap.put("url", env.getProperty("slave.datasource." + dsPrefix + ".url"));
                dsMap.put("username", env.getProperty("slave.datasource." + dsPrefix + ".username"));
                dsMap.put("password", env.getProperty("slave.datasource." + dsPrefix + ".password"));
                DataSource ds = buildDataSource(dsMap);
                slaveDataSources.put(dsPrefix, ds);
            }
        }
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //添加默認數據源
        targetDataSources.put("dataSource", this.defaultDataSource);
        DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
        //添加其他數據源
        targetDataSources.putAll(slaveDataSources);
        for (String key : slaveDataSources.keySet()) {
            DynamicDataSourceContextHolder.dataSourceIds.add(key);
        }

        //創建DynamicDataSource
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DynamicDataSource.class);
        beanDefinition.setSynthetic(true);
        MutablePropertyValues mpv = beanDefinition.getPropertyValues();
        mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
        mpv.addPropertyValue("targetDataSources", targetDataSources);
        //注冊 - BeanDefinitionRegistry
        beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);

        logger.info("Dynamic DataSource Registry");
    }

    public DataSource buildDataSource(Map<String, Object> dataSourceMap) {
        try {
            Object type = dataSourceMap.get("type");
            if (type == null) {
                type = DATASOURCE_TYPE_DEFAULT;// 默認DataSource
            }
            Class<? extends DataSource> dataSourceType;
            dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
            String driverClassName = dataSourceMap.get("driver").toString();
            String url = dataSourceMap.get("url").toString();
            String username = dataSourceMap.get("username").toString();
            String password = dataSourceMap.get("password").toString();
            // 自定義DataSource配置
            DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
                    .username(username).password(password).type(dataSourceType);
            return factory.build();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

動態數據源上下文管理:DynamicDataSourceContextHolder.java

public class DynamicDataSourceContextHolder {//存放當前線程使用的數據源類型信息
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    //存放數據源id
    public static List<String> dataSourceIds = new ArrayList<String>();

    //設置數據源
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    //獲取數據源
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    //清除數據源
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

    //判斷當前數據源是否存在
    public static boolean isContainsDataSource(String dataSourceId) {
        return dataSourceIds.contains(dataSourceId);
    }
}
動態數據源  DynamicDataSource.java  -- AbstractRoutingDataSource(每執行一次數據庫,動態獲取DataSource)
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
動態數據源通知 DynamicDataSourceAspect.java
@Aspect
@Order(-1)//保證在@Transactional之前執行
@Component
public class DynamicDataSourceAspect {//改變數據源
    public void changeDataSource( String  targetDataSource) {
        if (StringUtil.isNotEmpty(targetDataSource) && DynamicDataSourceContextHolder.isContainsDataSource(targetDataSource)) {
            DynamicDataSourceContextHolder.setDataSourceType(targetDataSource);
        }
    }

    public void clearDataSource( String  targetDataSource) {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }
}

 

換源API調用:

@Autowired
private DynamicDataSourceAspect dynamicDataSourceAspect;
使用: dynamicDattaSourceAspect.changeDataSource(##數據源名稱##);
-----查詢SQL業務----- dynamicDattaSourceAspect.clearDataSource(##數據源名稱##);

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM