druid驱动,springboot 整合mybatis动态多数据源

由于项目关联多类型的数据源,之前那个开发把每一种数据源都做成了一个Configuration,使用SqlSessionFactory的方式注入。调用的时候用sqlSessionFactory创建connection直接注入SQL,这种写法侵入性很大,所以今天将其重构为基于mybatis的动态数据源注入。

@Autowired
@Qualifier("mysqlSqlSessionFactory")
private SqlSessionFactory mysqlFactory;

创建数据源对象

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static Map<Object, Object> dataSourceMap = new HashMap<>();
    private static DynamicDataSource instance;
    private static byte[] lock=new byte[0];
 
    public Map<Object, Object> getDataSourceMap() {
        return dataSourceMap;
    }
 
    /**
     * 如果不希望数据源在启动配置时就加载好,可以定制这个方法,从任何你希望的地方读取并返回数据源
     * 比如从数据库、文件、外部接口等读取数据源信息,并最终返回一个DataSource实现类对象即可
     */
    @Override
    protected DataSource determineTargetDataSource() {
        return super.determineTargetDataSource();
    }
    /**
     * 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
 
    /**
     * 设置默认数据源
     * @param defaultDataSource
     */
    public void setDefaultDataSource(Object defaultDataSource) {
        super.setDefaultTargetDataSource(defaultDataSource);
    }
 
    /**
     * 设置数据源
     * @param dataSources
     */
    public void setDataSources(Map<Object, Object> dataSources) {
        super.setTargetDataSources(dataSources);
        // 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
        DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
    }
    
    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        dataSourceMap.putAll(targetDataSources);
        super.afterPropertiesSet();// 必须添加该句,否则新添加数据源无法识别到
    }
 
    public static synchronized DynamicDataSource getInstance(){
        if(instance==null){
            synchronized (lock){
                if(instance==null){
                    instance=new DynamicDataSource();
                }
            }
        }
        return instance;
    }
}

创建句柄切换数据源

package com.ggg.cheetah.common.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class DynamicDataSourceContextHolder {
    
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
        /**
         * 将 master 数据源的 key作为默认数据源的 key
         */
        @Override
        protected String initialValue() {
            return "default";
        }
    };

    /**
     * 数据源的 key集合,用于切换时判断数据源是否存在
     */
    public static List<Object> dataSourceKeys = new ArrayList<>();

    /**
     * 切换数据源
     * @param key
     */
    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    /**
     * 获取数据源
     * @return
     */
    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    /**
     * 重置数据源
     */
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }

    /**
     * 判断是否包含数据源
     * @param key 数据源key
     * @return
     */
    public static boolean containDataSourceKey(String key) {
        return dataSourceKeys.contains(key);
    }

    /**
     * 添加数据源keys
     * @param keys
     * @return
     */
    public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
        return dataSourceKeys.addAll(keys);
    }

}

注入数据源,扫描dao和mapper路径

@Configuration
@MapperScan(basePackages = {"com.ggg.cheetah.datagovernance.dao.mysqldb","com.ggg.cheetah.datagovernance.dao.oracledb","com.ggg.cheetah.datagovernance.dao.sqlserverdb"}, sqlSessionFactoryRef = "dynamicDataSourceFactory")
public class MybatisConfig {
    @Value("${spring.datasource.cheetahdb.url}")
    private String defaultDBUrl;
    @Value("${spring.datasource.cheetahdb.username}")
    private String defaultDBUser;
    @Value("${spring.datasource.cheetahdb.password}")
    private String defaultDBPassword;
    @Value("${spring.datasource.cheetahdb.driverClassName}")
    private String defaultDBDriverName;

    /**
     * 初始化之初,把本项目的数据源作为默认数据源注入
     *
     * @return
     */
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();

        DruidDataSource defaultDataSource = new DruidDataSource();
        defaultDataSource.setName("default");
        defaultDataSource.setUrl(defaultDBUrl);
        defaultDataSource.setUsername(defaultDBUser);
        defaultDataSource.setPassword(defaultDBPassword);
        defaultDataSource.setDriverClassName(defaultDBDriverName);

        Map<Object, Object> map = new HashMap<>();
        map.put("default", defaultDataSource);
        dynamicDataSource.setTargetDataSources(map);
        dynamicDataSource.setDefaultDataSource(defaultDataSource);

        return dynamicDataSource;
    }

    @Bean(name = "dynamicDataSourceFactory")
    public SqlSessionFactory sqlSessionFactory(
            @Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(
                // 设置mybatis的xml所在位置
                new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/*db/*Mapper.xml"));
        return bean.getObject();

    }

    @Bean(name = "dynamicDataSourceTemplate")
    public SqlSessionTemplate sqlSessionTemplate(
            @Qualifier("dynamicDataSourceFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

service里有两个方法 一个返回所有查询出的数据源信息 driver name url username password

另一个方法设置数据源,将所有数据源注入map,用于切换

    @Override
    public void setDataSourceInfo() {
        DynamicDataSourceContextHolder.setDataSourceKey("default");
        List<DruidDataSource> list = this.getList();
        //使用自己设置的sourceName作为数据源的key,使用这个进行切换
        Map<Object, Object> dataSourceMap = list.stream().collect(Collectors.toMap(x -> x.getName(), x -> {
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(x.getDriverClassName());
            druidDataSource.setUrl(x.getUrl());
            druidDataSource.setUsername(x.getUsername());
            druidDataSource.setPassword(x.getPassword());
            druidDataSource.setKeepAlive(true);
            return druidDataSource;
        }));
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();
        Map<Object, Object> map = dynamicDataSource.getDataSourceMap();
        map.putAll(dataSourceMap);
        dynamicDataSource.setTargetDataSources(map);
        dynamicDataSource.afterPropertiesSet();
    }
这样就可以动态进行切换了
DynamicDataSourceContextHolder.setDataSourceKey("aaa");

最后调用数据源注入的SQL进行查询,由于是多种数据源,需要建立不同数据源对应的mapper,存放适用的SQL语句。

数据库读写分离也可以用这个思路做。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值