Mybatis動態數據源切換

Mybatis動態數據源切換

1. 結構設計

首先看一下這個功能的架構設計
動態數據源結構設計java

  • 咱們默認有一個缺省的數據源 Deault DataSource ,他是從配置文件中獲取的,在應用剛開始啓動時就注入,而在某些狀況下,咱們須要在一次操做中短時或長時間的對其它的數據庫進行操做,這就是所謂的數據源切換。
  • 爲了保證新添加的數據源不會對其它線程的操做有英影響,咱們使用ThreadLocal來存儲當前使用的數據源的相關信-息,建立上下文 DataSourceContextHolder 類,來保留當前線程的數據源。
  • 在Mybatis讀取配置建立 Session 時,會注入 DynamicDataSource ,而 DynamicDataSource 經過讀取當前線程變量能夠獲取本身設置的數據源,若是沒有設置會注入默認的數據源,這個數據源來自於配置文件,調用流程以下圖

動態數據源調用過程

2. 實現

public class DataSourceContextHolder {
    // 當前線程使用的數據源,爲null表示默認數據源
    private static final ThreadLocal<DataSource> contextHolder = new InheritableThreadLocal<DataSource>();
    // 當前線程使用過的數據源,方便事務
    private static final List<DataSource> dataSources = new ArrayList<>();
    // 全局外部數據源緩存
    private static final HashMap<String, DataSource> map = new HashMap<>();

    // 設置當前線程的數據源
    public static void setDataSource(DruidDataSource datasource) {
        if (!map.containsKey(datasource.getUrl())) {
            contextHolder.set(datasource);
            map.put(datasource.getUrl(), datasource);
        }
        else {
            contextHolder.set(map.get(datasource.getUrl()));
        }
        dataSources.add(contextHolder.get());
    }

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

    // 獲取數據源
    public static List<DataSource> getThreadDataSources() {
        return dataSources;
    }

    public static void clearCache() {
        map.clear();
    }

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

}
@Configuration
public class DataSourceConfig {
    
    
    @Bean(name = "defaultDataSource")
    @ConfigurationProperties(prefix = "jdbc")
    public DataSource defaultDataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dynamicDataSource() {
        return new DynamicDataSource();
    }
}
/**
 * 動態數據源管理類
 */
public class DynamicDataSource extends AbstractDataSource {
    
    // 注入默認數據源
    @Resource(name = "defaultDataSource")
    private DataSource defaultDs;

    protected DataSource determineTargetDataSource() {
        // 獲取當前線程的數據源
        DataSource dataSource = DataSourceContextHolder.getDataSource();
        // 若是沒有設置動態數據源,則返回默認數據源
        if (dataSource == null) {
            return defaultDs;
        }
        return dataSource;
    }

    @Override
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }
}
@Configuration
public class MyBatisConfig {

    @Resource(name = "dynamicDataSource")
    private DynamicDataSource dynamicDataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setObjectWrapperFactory(new MapWrapperFactory());
        // 設置數據源爲DynamicDataSource
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("me.ezerror.pojo");
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate financialMasterSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

3.使用方式

// 新建數據源
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setPassword(pwd);
dataSource.setUsername(username);
// 設置數據源
DataSourceContextHolder.setDataSource(dataSource);
// 數據庫操做
List<Moment> moments = recordService.findMoment();
// 清除數據源,還原到默認數據源
DataSourceContextHolder.clearDataSource();
相關文章
相關標籤/搜索