springboot 之 根据传入参数进行多数据源动态切换
背景:最近有一个需求是根据app传来的请求参数,根据行政部门编码请求不同地区的数据,之前写的多数据源都是固定某个方法调用指定的dao然后查询不同的数据库,但是这次是需要根据前端传入参数进行动态区分数据库,所以就需要做特殊处理
1.注册多数据源:
@Configuration public class DataSourceConfiguration { /** * 交管局数据源 */ @Bean(name = "jiaoguanjuDataSource") @Qualifier("jiaoguanjuDataSource") @ConfigurationProperties(prefix="spring.datasource.jiaoguanju") public DataSource jiaoguanjuDataSource() { return DataSourceBuilder.create().build(); } /** * 广州数据源 */ @Bean(name = "guangzhouDataSource") @Qualifier("guangzhouDataSource") @ConfigurationProperties(prefix="spring.datasource.guangzhou") public DataSource guangzhouDataSource() { return DataSourceBuilder.create().build(); } /** * 清远数据源 */ @Bean(name = "qingyuanDataSource") @Qualifier("qingyuanDataSource") @ConfigurationProperties(prefix="spring.datasource.qingyuan") public DataSource qingyuanDataSource() { return DataSourceBuilder.create().build(); } /** * 韶关数据源 */ @Bean(name = "shaoguanDataSource") @Qualifier("shaoguanDataSource") @ConfigurationProperties(prefix="spring.datasource.shaoguan") public DataSource shaoguanDataSource() { return DataSourceBuilder.create().build(); } /** * cancl数据源 */ @Bean(name = "secondaryDataSource") @Qualifier("secondaryDataSource") @ConfigurationProperties(prefix="spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") @Primary public DataSource dynamicDataSource(){ DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.myMap = new HashMap<>();//保存我们有的数据源,方便后面动态增加 dynamicDataSource.myMap.put("guangzhou",guangzhouDataSource()); dynamicDataSource.myMap.put("qingyuan",qingyuanDataSource()); dynamicDataSource.myMap.put("shaoguan",shaoguanDataSource()); dynamicDataSource.myMap.put("jiaoguanju", jiaoguanjuDataSource()); // dynamicDataSource.myMap.put("3",thirdDataSource()); dynamicDataSource.setTargetDataSources(dynamicDataSource.myMap);//父类的方法 DynamicDataSourceContextHolder.dataSourceIds.addAll(dynamicDataSource.myMap.keySet()); dynamicDataSource.setDefaultTargetDataSource(guangzhouDataSource());//父类的方法 return dynamicDataSource; } }
2.将数据源交给AbstractRoutingDataSource
/** * @Author Cheng ZhiHua * @Date 2019-11-05 16:01 * @Description 核心方法 :继承AbstractRoutingDataSource 类,将数据源交给AbstractRoutingDataSource进行注入使用 **/ @Slf4j public class DynamicDataSource extends AbstractRoutingDataSource { public Map<Object,Object> myMap = null; @Override protected Object determineCurrentLookupKey() { /* * DynamicDataSourceContextHolder代码中使用setDataSourceType * 设置当前的数据源,在路由类中使用getDataSourceType进行获取, * 交给AbstractRoutingDataSource进行注入使用。 */ // log.info("数据源为: {}",DynamicDataSourceContextHolder.getDataSourceType()); return DynamicDataSourceContextHolder.getDataSourceType(); } }
3.每个请求与线程绑定,保证各个请求之前互不影响
/** * @Author Cheng ZhiHua * @Date 2019-11-05 16:02 * @Description **/ public class DynamicDataSourceContextHolder { /* * 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本, * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 */ private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static List<Object> dataSourceIds = new ArrayList<Object>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } public static boolean containsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
4.调用一定要在事务之前,在controller层
/**
* 设置当前线程的数据库连接
*
* @param data
*/
private void ThreadLocalParamSet(Map<String, Object> data) { Map<String, String> userInfo = (Map<String, String>) data.get("userInfo"); String dataSourceType = deptnoReleaseDatasourceMap.get(userInfo.get("userDeptNo").substring(0, 4)); DynamicDataSourceContextHolder.setDataSourceType(dataSourceType); }