2020年9月17日 作者 zeroheart

springboot的多数据源

public class DynamicDataSource extends AbstractRoutingDataSource
{
private Map<Object, Object> targetDataSources;//实现动态添加

public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
this.targetDataSources = targetDataSources;
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}

@Override
protected Object determineCurrentLookupKey()
{
return DynamicDataSourceContextHolder.getDataSourceType();
}

public Map<Object, Object> getTargetDataSources() {
return targetDataSources;
}

}
1.搞一个类继承AbstractRoutingDataSource,把这个targetDataSources暴露出来,在 filter里面可以对它进行动态添加的操作

@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties(“spring.datasource.druid.master”)
public DataSource masterDataSource(DruidProperty druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}

@Bean(name = “dynamicDataSource”)
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
return new DynamicDataSource(masterDataSource, targetDataSources);
}
}
2.搞一个config,初始化一下这个dynamicDataSource

//切换到新的数据源
Map m = apps.get(0);
String driver = “com.mysql.jdbc.Driver”;
String host = (String) m.get(“f_dbhost”);
String user = (String) m.get(“f_dbusername”);
String dbname = (String) m.get(“f_dbname”);
String password = (String) m.get(“f_dbpassword”);
DruidDataSource newDataSource = druidProperty.dataSource(new DruidDataSource());
newDataSource.setDriverClassName(driver);
String url = “jdbc:mysql://”+host+”/”+dbname+”?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8″;
newDataSource.setUrl(url);
newDataSource.setUsername(user);
newDataSource.setPassword(password);

//动态添加新的租户
tgetTargetDataSources.put(domain, newDataSource);
dynamicDataSource.setTargetDataSources(tgetTargetDataSources);
dynamicDataSource.afterPropertiesSet();

DynamicDataSourceContextHolder.setDataSourceType(domain);

3.写一个拦截器,根据情况处理,存在的租户,直接切换,新加的租户动态添加,然后执行afterPropertiesSet,相当于刷新数据源列表。

4.DynamicDataSourceContextHolder的代码也存一下。
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType)
{
log.info(“切换到{}数据源”, dsType);
CONTEXT_HOLDER.set(dsType);
}

/**
* 获得数据源的变量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}

/**
* 清空数据源变量
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}