1.修改properties(spring默認的)數據源的前綴 新增中台數據
2編寫數據源上下文
mport java.util.List;
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<String>();
public static final String DEFAULT_DS ="defaultDs";
/*
* 管理所有的數據源id;
* 主要是為了判斷數據源是否存在;
*/
public static List<String> dataSourceIds = new ArrayList<String>();
// 設置數據源類型
public static void setDataSourceType(String datasource) {
contextHolder.set(datasource);
}
// 獲取數據源類型
public static String getDataSourceType() {
return (String) contextHolder.get();
}
// 清除數據源類型
public static void clearDataSourceType() {
contextHolder.remove();
}
//判斷指定DataSource當前是否存在
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
3 繼承AbstractRoutingDataSource實現類DynamicDataSource(關鍵)
AbstractRoutingDataSource抽象類知識,實現AOP動態切換的關鍵
1.AbstractRoutingDataSource中determineTargetDataSource()方法中獲取數據源
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
根據determineCurrentLookupKey()得到Datasource,並且此方法是抽象方法,應用可以實現
2.resolvedDataSources的值根據targetDataSources所得
afterPropertiesSet()方法中(在@Bean所在方法執行完成后,會調用此方法):
Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()
3.然后在xml中使用<bean>或者代碼中@Bean 設置DynamicDataSource的defaultTargetDataSource(默認數據源)和targetDataSources(多數據源)
4.利用自定義注解,AOP攔截動態的設置ThreadLocal的值
5.在DAO層與數據庫建立連接時會根據ThreadLocal的key得到數據源
代碼:getConnection()
determineTargetDataSource().getConnection();(determineTargetDataSource返回的是DataSource)
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
根據determineCurrentLookupKey()得到Datasource,並且此方法是抽象方法,應用可以實現
2.resolvedDataSources的值根據targetDataSources所得
afterPropertiesSet()方法中(在@Bean所在方法執行完成后,會調用此方法):
Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()
3.然后在xml中使用<bean>或者代碼中@Bean 設置DynamicDataSource的defaultTargetDataSource(默認數據源)和targetDataSources(多數據源)
4.利用自定義注解,AOP攔截動態的設置ThreadLocal的值
5.在DAO層與數據庫建立連接時會根據ThreadLocal的key得到數據源
代碼:getConnection()
determineTargetDataSource().getConnection();(determineTargetDataSource返回的是DataSource)
package com.lm.config.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
4配置多數據源
@Configuration
public class DataSourceConfig {
@Bean(name = "defaultDs")
@ConfigurationProperties(prefix = "jph.datasource") // application.properteis中對應屬性的前綴
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ziKuDs")
@ConfigurationProperties(prefix = "ziku.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
/**
* 動態數據源: 通過AOP在不同數據源之間動態切換
* @return
*/
@Bean(name = "dynamicDS")
@Primary //優先使用
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默認數據源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
// 配置多數據源
Map<Object, Object> dsMap = new HashMap(4);
dsMap.put("defaultDs", dataSource1());
dsMap.put("ziKuDs", dataSource2());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
}5配置mybatis的核心類 (SqlSessionTemplate)1)精品匯2)中台@Configuration
@MapperScan(basePackages = {"com.lm.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory1")
public class jphDbConfig {
@Autowired
@Qualifier("defaultDs")
private DataSource defaultDs;
@Bean
@Primary
public SqlSessionFactory sqlSessionFactory1() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(defaultDs);
return factoryBean.getObject();
}
@Bean
@Primary
public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
return template;
}
}
6 為了方便使用 我們使用aop加注解的方式來切換數據源
@Configuration
@MapperScan(basePackages = {"com.ziku.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory2")
public class ZikuDbConfig {
@Autowired
@Qualifier("ziKuDs")
private DataSource zikuDs;
@Bean
public SqlSessionFactory sqlSessionFactory2() throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(zikuDs); // 使用titan數據源, 連接titan庫
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2()); // 使用上面配置的Factory
return template;
}
}
/**
* 在方法上使用,用於指定使用哪個數據源
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value() default "defaultDs";
}
/**
* 切換數據源Advice
*/
@Aspect
@Order(-10)//保證該AOP在@Transactional之前執行
@Component
public class DynamicDataSourceAspect {
/*
* @Before("@annotation(TargetDataSource)")
* 的意思是:
* @Before:在方法執行之前進行執行:
* @annotation(targetDataSource):
* 會攔截注解targetDataSource的方法,否則不攔截;
*/
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Before("@annotation(TargetDataSource)")
public void beforeSwitchDS(JoinPoint point){
//獲得當前訪問的class
Class<?> className = point.getTarget().getClass();
//獲得訪問的方法名
String methodName = point.getSignature().getName();
//得到方法的參數的類型
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DS;
try {
// 得到訪問的方法對象
Method method = className.getMethod(methodName, argClass);
// 判斷是否存在@DS注解
if (method.isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
// 取出注解中的數據源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切換數據源
DataSourceContextHolder.setDataSourceType(dataSource);
}
@After("@annotation(TargetDataSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDataSourceType();
}
}
