1. 程式人生 > 實用技巧 >springboot 之 根據傳入引數進行多資料來源動態切換

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);
}