有的時(shí)候我們的代碼同時(shí)會(huì)用到多個(gè)數(shù)據(jù)庫,因此需要在請(qǐng)求mysql前,切換數(shù)據(jù)源
通過aop+注解的方式能輕松實(shí)現(xiàn)多數(shù)據(jù)源
這里介紹使用spring+aspectj自動(dòng)代理+注解的方式實(shí)現(xiàn)mybatis多數(shù)據(jù)源的切換
知識(shí)點(diǎn)
- 數(shù)據(jù)源路由器AbstractRoutingDataSource,繼承該類的數(shù)據(jù)源,每次請(qǐng)求時(shí)都會(huì)通過determineCurrentLookupKey方法決定使用的數(shù)據(jù)源
- @annotation切入點(diǎn)表達(dá)式
多數(shù)據(jù)源
首先我們需要?jiǎng)?chuàng)建多個(gè)鏈接池(數(shù)據(jù)源)
/** DynamicDataSourceProperties.java **/
//配置類
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/**
* 數(shù)據(jù)源配置,這里繼承了springboot的單數(shù)據(jù)源配置類,并且為配置分配了key
*/
@Component
@Primary
@ConfigurationProperties("datasource.dynamic.properties")
public class DynamicDataSourceProperties extends DataSourceProperties {
private String key;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
/** DynamicDataSourceRegisterProperties.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 動(dòng)態(tài)多數(shù)據(jù)源配置文件
*/
@Component
@ConfigurationProperties("datasource.dynamic.register")
public class DynamicDataSourceRegisterProperties {
public String defaultKey;
public List<DynamicDataSourceProperties> properties;
public List<DynamicDataSourceProperties> getProperties() {
return properties;
}
public void setProperties(List<DynamicDataSourceProperties> properties) {
this.properties = properties;
}
public String getDefaultKey() {
return defaultKey;
}
public void setDefaultKey(String defaultKey) {
this.defaultKey = defaultKey;
}
}
# application.properties
# 多數(shù)據(jù)源配置
# 第一個(gè)數(shù)據(jù)源
datasource.dynamic.register.properties[0].key=xss
datasource.dynamic.register.properties[0].url=jdbc:mysql://127.0.0.1:3306/xss
datasource.dynamic.register.properties[0].username=root
datasource.dynamic.register.properties[0].password=**********
datasource.dynamic.register.properties[0].driver-class-name=com.mysql.cj.jdbc.Driver
# 第二個(gè)數(shù)據(jù)源
datasource.dynamic.register.properties[1].key=forecast
datasource.dynamic.register.properties[1].url=jdbc:mysql://127.0.0.1:3306/forecast
datasource.dynamic.register.properties[1].username=root
datasource.dynamic.register.properties[1].password=**********
datasource.dynamic.register.properties[1].driver-class-name=com.mysql.cj.jdbc.Driver
/** DynamicDataSourceContextHolder.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* 用來存儲(chǔ)數(shù)據(jù)源的key
*/
public class DynamicDataSourceContextHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 存儲(chǔ)已經(jīng)注冊(cè)的數(shù)據(jù)源的key,所有配置的數(shù)據(jù)源
*/
public static List<String> dataSourceIds = new ArrayList<>();
/**
* 線程級(jí)別的私有變量,當(dāng)前線程的數(shù)據(jù)源key
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
public static String getDataSourceRouterKey () {
return HOLDER.get();
}
public static void setDataSourceRouterKey (String dataSourceRouterKey) {
logger.info("切換至{}數(shù)據(jù)源", dataSourceRouterKey);
HOLDER.set(dataSourceRouterKey);
}
/**
* 設(shè)置數(shù)據(jù)源之前一定要先移除
*/
public static void removeDataSourceRouterKey () {
HOLDER.remove();
}
/**
* 判斷指定DataSrouce當(dāng)前是否存在
*
* @param dataSourceId 數(shù)據(jù)源ID
* @return 是否存在Datasource
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
/** DynamicRoutingDataSource.java **/
package com.sinbxeunha.josechan.dynamicDataSource.register;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceContextHolder;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceProperties;
import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceRegisterProperties;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 動(dòng)態(tài)數(shù)據(jù)源注冊(cè)
* 實(shí)現(xiàn) ImportBeanDefinitionRegistrar 實(shí)現(xiàn)數(shù)據(jù)源注冊(cè)
* 實(shí)現(xiàn) EnvironmentAware 用于讀取application.yml配置
*/
@Component
public class DynamicDataSourceRegister implements InitializingBean {
/** 自動(dòng)注入配置類 */
@Autowired
DynamicDataSourceRegisterProperties dynamicDataSourceRegisterProperties;
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
/**
* 別名
*/
private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
static {
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
}
/**
* 存儲(chǔ)我們注冊(cè)的數(shù)據(jù)源
*/
private final Map<Object, Object> customDataSources = new HashMap<Object, Object>();
/** 記錄默認(rèn)數(shù)據(jù)源 */
private DataSource defaultDatasource = null;
/**
* 繼承自InitializingBean,對(duì)象生成后自動(dòng)執(zhí)行
* @throws Exception 異常
*/
@Override
public void afterPropertiesSet() throws Exception {
List<DynamicDataSourceProperties> properties = dynamicDataSourceRegisterProperties.getProperties();
String defaultKey = dynamicDataSourceRegisterProperties.getDefaultKey();
for (DynamicDataSourceProperties property : properties){
DataSourceBuilder<?> builder = property.initializeDataSourceBuilder();
DataSource dataSource = builder.type(property.getType()).build();
customDataSources.put(property.getKey(), dataSource);
DynamicDataSourceContextHolder.dataSourceIds.add(property.getKey());
if(property.getKey().equals(defaultKey)){
defaultDatasource = dataSource;
}
}
}
public Map<Object, Object> getCustomDataSources() {
return customDataSources;
}
public DataSource getDefaultDatasource() {
return defaultDatasource;
}
}
/** DynamicRoutingDataSource.java **/
package com.sinbxeunha.josechan.dynamicDataSource.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 動(dòng)態(tài)數(shù)據(jù)源類,繼承了AbstractRoutingDataSource,實(shí)現(xiàn)其中的determineCurrentLookupKey
* 該方法會(huì)在每次查詢前被調(diào)用,用于獲取數(shù)據(jù)源
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
logger.info("當(dāng)前數(shù)據(jù)源是:{}", dataSourceName);
return DynamicDataSourceContextHolder.getDataSourceRouterKey();
}
}