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

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

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

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

  再新增一个方法:refreshDataSources

private void refreshDataSources(List
 databaseList) {    if (databaseList != null && databaseList.size() > 0) {        BasicDataSource templateDataSource = (BasicDataSource) this.context.getBean("dataSource");//模板数据源        Map
 targetDataSources = new HashMap
();        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的配置方法,只是多了一个数据源的配置

   
       
           
       
   
   

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

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

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

下面是DataSourceContextHolder的代码

public class DataSourceContextHolder {    private static final ThreadLocal
 contextHolder = new ThreadLocal
();    public static void setDataSourceType(String dataSourceType) {        contextHolder.set(dataSourceType);    }    public static String getDataSourceType() {        return contextHolder.get();    }    public static void clearDataSourceType() {        contextHolder.remove();    }}

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