Spring+Mybatis项目中通过继承AbstractRoutingDataSource实现数据库热切换

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring+Mybatis项目中通过继承AbstractRoutingDataSource实现数据库热切换相关的知识,希望对你有一定的参考价值。

  在做ERP项目的时候有个需求是能够管理和切换账套,一个账套就是一个数据库,那么就需要实现数据库的热切换。网上找了很多资料再结合项目的具体需求实现了一个还算比较好用的数据库热切换。

  原理是首先继承AbstractRoutingDataSource并实现determineCurrentLookupKey方法,方法的内容为

protected Object determineCurrentLookupKey() {
    return DataSourceContextHolder.getDataSourceType();
}

这个方法的功能是再执行sql之前spring会先执行这个方法,并从数据源Map中通过这个方法返回的key去确定要使用的数据源。

  再新增一个方法:refreshDataSources

private void refreshDataSources(List<String> databaseList) {
    if (databaseList != null && databaseList.size() > 0) {
        BasicDataSource templateDataSource = (BasicDataSource) this.context.getBean("dataSource");//模板数据源

        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        for (String database : databaseList) {
            BasicDataSource dataSource = new BasicDataSource();
            dataSource.setDriverClassName(templateDataSource.getDriverClassName());
            dataSource.setUrl(regexReplaceUrl(templateDataSource.getUrl(), database));
            dataSource.setUsername(templateDataSource.getUsername());
            dataSource.setPassword(templateDataSource.getPassword());
            dataSource.setMaxActive(templateDataSource.getMaxActive());
            dataSource.setMaxIdle(templateDataSource.getMaxIdle());
            dataSource.setDefaultAutoCommit(templateDataSource.getDefaultAutoCommit());
            dataSource.setTimeBetweenEvictionRunsMillis(templateDataSource.getTimeBetweenEvictionRunsMillis());
            dataSource.setMinEvictableIdleTimeMillis(templateDataSource.getMinEvictableIdleTimeMillis());
            targetDataSources.put(database, dataSource);
        }
        targetDataSources.put("sycerp_system", templateDataSource); //加入默认dataSource

        this.setTargetDataSources(targetDataSources);
        this.setDefaultTargetDataSource(targetDataSources.get("sycerp_system"));
    }
    super.afterPropertiesSet();
}


// 这个方法的功能是将已经配置好的默认数据源的url中的database段替换会databaseList中的字符串
private String regexReplaceUrl(String url, String database) {
    return Pattern.compile("[/][a-z0-9_]+[\\?]").matcher(url).replaceAll("/" + database + "?");
}

这个方法的功能是将传入的databaseList(需要热切换的数据库名称列表),除了datasource的url需要替换外其他的属性都通过默认配置好的数据源中配置的属性,最后将封装好的数据源集合放入targetDataSources中,然后调用afterPropertiesSet方法刷新数据源,afterPropertiesSet的具体内容可以查看源码。

其中,这段代码中的context对象是通过实现ApplicationContextAware接口的setApplicationContext方法取得的,spring会自动注入applicationContext对象。

  然后就是配置,我们还是按照常规的spring整合mybatis的配置方法,只是多了一个数据源的配置

<bean id="dynamicDataSource" class="cn.jteee.sycerp.server.datasources.DynamicDataSource">
   <property name="targetDataSources">
       <map>
           <entry value-ref="dataSource" key="sycerp_system"/>
       </map>
   </property>
   <property name="defaultTargetDataSource" ref="dataSource"/>
</bean>

其中dynamicDataSource就是上面实现AbstractRoutingDataSource的类,里面ref的dataSource就是默认的数据源,其他配置数据ref的地方就使用这个dynamicDataSource,而不是dataSource。

  最后就是怎么使用。因为dynamicDataSource已经被spring管理起来了,所以,我们只需要在我们会用到的地方先生成一个数据库名称列表,可以从其他数据库中查询,然后调用refreshDataSources方法,最后调用

DataSourceContextHolder.setDataSourceType("你要切换的数据库名称");

下面是DataSourceContextHolder的代码

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

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

}

  到此,一个完整的实现数据库热切换的功能就全部实现了,有其他特定的需求可以在此基础上修改。

本文出自 “伯彧” 博客,请务必保留此出处http://chrischen.blog.51cto.com/9954795/1878517

以上是关于Spring+Mybatis项目中通过继承AbstractRoutingDataSource实现数据库热切换的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud项目中通过Feign进行内部服务调用发生401407错误无返回信息的问题

Spring Cloud项目中通过Feign进行内部服务调用发生401407错误无返回信息的问题

spring cloud实战 1-高可用注册中心

如何使用存储库接口在 Spring Data 中通过其嵌套对象的 objectId 查找集合?

在平行继承层次结构中通过父母关联孩子

11JavaScript中通过prototype实现继承