背景:最近有一個需求是根據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); }
