SpringBoot的多資料來源配置
阿新 • • 發佈:2018-12-18
在專案中需要在不同的IP裡的資料庫獲取資料,所以要求要可以靈活的指定具體要操作的資料庫。
主要使用的框架是spring-boot+mybatis等。
一:先將maven專案配置好(略);
二:springboot的啟動類:
import org.apache.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@Import(DynamicDataSourceRegister.class)
@SpringBootApplication
public class Application {
private static Logger logger = Logger.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三:SpringBoot多資料來源的配置:
1.springboot的資料來源屬性檔案:
//預設的資料來源
spring.datasource.url=jdbc:mysql://138.0.0.1:4545/test1
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
//配置的第二個資料來源
custom.datasource.names=test2
custom.datasource.new.url=jdbc:mysql://138.0.0.1:5656/test2
custom.datasource.new .username=root
custom.datasource.new.password=root
custom.datasource.new.driver-class-name=com.mysql.jdbc.Driver
2 .編寫一個動態資料來源類:
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName DynamicDataSourceContextHolder
* @Description 判斷當前資料來源是否存在(上下文)
*/
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
public static List<String> dataSourceIds = new ArrayList<>();
//使用setDataSourceType設定當前的
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
//判斷指定DataSrouce當前是否存在
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
3.動態資料來源:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @ClassName DynamicDataSource
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
//設定當前的資料來源,在路由類中使用getDataSourceType進行獲取,
// 交給AbstractRoutingDataSource進行注入使用。
@Override
protected Object determineCurrentLookupKey() {
// TODO Auto-generated method stub
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
4.指定資料來源註解類:
/**
* @ClassName DynamicDataSourceAspect
* @Description 切換資料來源Advice
*/
@Aspect
@Order(-1)//保證該AOP在@Transactional之前執行
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(Source)")
public void changeDataSource(JoinPoint point, TargetDataSource Source) throws Throwable {
String dsId = Source.name();
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
logger.error("資料來源[{}]不存在,使用預設資料來源 > {}", ds.name(), point.getSignature());
} else {
logger.debug("Use DataSource : {} > {}", Source.name(), point.getSignature());
DynamicDataSourceContextHolder.setDataSourceType(ds.name());
}
}
@After("@annotation(Source)")
public void restoreDataSource(JoinPoint point, TargetDataSource Source) {
logger.debug("Revert DataSource : {} > {}", Source.name(), point.getSignature());
//方法執行完畢之後,銷燬當前資料來源資訊,進行垃圾回收。
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
5.註冊多資料來源:
以上都是動態資料來源在注入的時候使用的程式碼,其實很重要的一部分程式碼就是註冊我們在application.properties配置的多資料來源;
/**
* @ClassName DynamicDataSourceRegister
* @Description 動態資料來源註冊
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
// 如配置檔案中未指定資料來源型別,使用該預設值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
// private static final Object DATASOURCE_TYPE_DEFAULT =
// "com.zaxxer.hikari.HikariDataSource";
// 資料來源
private DataSource defaultDataSource;
private Map<String,DataSource> customDataSources = new HashMap<>();
/**
* 建立DataSource
*
* @param type
* @param driverClassName
* @param url
* @param username
* @param password
* @return
*/
@SuppressWarnings("unchecked")
public DataSource buildDataSource(Map<String,Object> dsMap) {
try {
Object type = dsMap.get("type");
if (type == null)
type = DATASOURCE_TYPE_DEFAULT;// 預設DataSource
Class dataSourceType;
dataSourceType = (Class) Class.forName((String) type);
String driverClassName = dsMap.get("driver-class-name").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type(dataSourceType);
return factory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 載入多資料來源配置
*/
@Override
public void setEnvironment(Environment env) {
initDefaultDataSource(env);
initCustomDataSources(env);
}
/**
* 初始化主資料來源
*
*/
public void initDefaultDataSource(Environment env) {
// 讀取主資料來源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
Map<String,Object> dsMap = new HashMap<>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
defaultDataSource = buildDataSource(dsMap);
}
/**
* 初始化更多資料來源
*
*/
public void initCustomDataSources(Environment env) {
// 讀取配置檔案獲取更多資料來源,也可以通過defaultDataSource讀取資料庫獲取更多資料來源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
for (String dsPrefix : dsPrefixs.split(",")) {// 多個數據源
Map<String,Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
customDataSources.put(dsPrefix, buildDataSource(dsMap));
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// TODO Auto-generated method stub
Map<Object,Object> targetDataSources = new HashMap<Object,Object>();
// 將主資料來源新增到更多資料來源中
targetDataSources.put("dataSource", defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
// 新增更多資料來源
targetDataSources.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
}
// 建立DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
logger.info("Dynamic DataSource Registry");
}
}
6.新增使用的標籤:
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
/**
* @ClassName TargetDataSource
* @Description 在方法上使用,用於指定使用哪個資料來源
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String name();
}
現在已經完成了多資料來源的配置,現在就是使用了;
寫好mybatis的查詢方法後,呼叫如下:
@Override
public void slselecttest1() {
// TODO Auto-generated method stub
//mybatis查詢 test1資料庫的方法
CommunityAnalysisDao.slselecttest1();
}
@Override
@TargetDataSource(name="test2")//test2是你資料來源配置屬性檔案的names
public void slselecttest2() {
// TODO Auto-generated method stub
//mybatis查詢 test2資料庫的方法
CommunityAnalysisDao.slselecttest2();
}
這樣就可以跨庫查詢了。
本文參考:http://blog.csdn.net/catoop/article/details/50575038
http://blog.csdn.net/linxingliang/article/details/52324937