Springboot+mybatis動態設置數據源


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.AbstractRoutingDataSourcedetermineTargetDataSource()方法中獲取數據源 
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)精品匯
@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;
}
}

2)中台

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

6 為了方便使用 我們使用aop加注解的方式來切換數據源
/**

* 在方法上使用,用於指定使用哪個數據源
*/

@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();

}

}












免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM