背景
最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据。考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案。在此分享给大家。
实现方案
数据库配置文件
我们项目使用的是yml形式的配置文件,采用的是hikari的数据库连接池。第一步我们自然是配置多个数据库源头。
我们找到spring的datasource,在下方配置三个数据源。
-
spring:
-
application:
-
name: dynamicDatasource
-
datasource:
-
test1:
-
driver-class-name: com.mysql.jdbc.Driver
-
url: jdbc:mysql://127.0.0.1:3306/test1?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
-
username: root
-
password: 123456
-
test2:
-
driver-class-name: com.mysql.jdbc.Driver
-
url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
-
username: root
-
password: 123456
-
test3:
-
driver-class-name: com.mysql.jdbc.Driver
-
url: jdbc:mysql://127.0.0.1:3306/test3?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
-
username: root
-
password: 123456
-
hikari:
-
leak-detection-threshold: 2000
定义数据源实体类
我们可以建立个datasourceBean文件夹专门管理数据源的实体类。
我们这里要建立三个实体类。分别对应test1,test2,test3
-
@Configuration
-
public class Test1DataSourceBean {
-
-
@Value("${spring.datasource.test1.driver-class-name}")
-
private String test1Driver;
-
-
@Value("${spring.datasource.test1.url}")
-
private String test1Url;
-
-
@Value("${spring.datasource.test1.username}")
-
private String test1Username;
-
-
@Value("${spring.datasource.test1.password}")
-
private String test1Password;
-
-
@Bean(name="test1DataSource")
-
public DataSource test1DataSource() throws Exception{
-
HikariDataSource dataSource = new HikariDataSource();
-
dataSource.setDriverClassName(test1Driver);
-
dataSource.setJdbcUrl(test1Url);
-
dataSource.setUsername(test1Username);
-
dataSource.setPassword(test1Password);
-
return dataSource;
-
}
-
}
-
@Configuration
-
public class Test2DataSourceBean {
-
-
@Value("${spring.datasource.test2.driver-class-name}")
-
private String test2Driver;
-
-
@Value("${spring.datasource.test2.url}")
-
private String test2Url;
-
-
@Value("${spring.datasource.test2.username}")
-
private String test2Username;
-
-
@Value("${spring.datasource.test2.password}")
-
private String test2Password;
-
-
@Bean(name="test2DataSource")
-
public DataSource test2DataSource() throws Exception{
-
HikariDataSource dataSource = new HikariDataSource();
-
dataSource.setDriverClassName(test2Driver);
-
dataSource.setJdbcUrl(test2Url);
-
dataSource.setUsername(test2Username);
-
dataSource.setPassword(test2Password);
-
return dataSource;
-
}
-
}
-
@Configuration
-
public class Test3DataSourceBean {
-
-
@Value("${spring.datasource.test3.driver-class-name}")
-
private String test3Driver;
-
-
@Value("${spring.datasource.test3.url}")
-
private String test3Url;
-
-
@Value("${spring.datasource.test3.username}")
-
private String test3Username;
-
-
@Value("${spring.datasource.test3.password}")
-
private String test3Password;
-
-
@Bean(name="test3DataSource")
-
public DataSource test3DataSource() throws Exception{
-
HikariDataSource dataSource = new HikariDataSource();
-
dataSource.setDriverClassName(test3Driver);
-
dataSource.setJdbcUrl(test3Url);
-
dataSource.setUsername(test3Username);
-
dataSource.setPassword(test3Password);
-
return dataSource;
-
}
-
}
定义一个枚举类管理数据源
-
public enum DatabaseType {
-
-
test1("test1", "test1"),
-
test2("test2", "test2"),
-
test3("test3","test3");
-
-
private String name;
-
private String value;
-
-
DatabaseType(String name, String value){
-
this.name = name;
-
this.value = value;
-
}
-
-
public String getName(){
-
return name;
-
}
-
-
public String getValue(){
-
return value;
-
}
-
}
定义一个线程安全的数据源容器
-
public class DatabaseContextHolder {
-
-
private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();
-
-
public static void setDatabaseType(DatabaseType type){
-
contextHolder.set(type);
-
}
-
-
public static DatabaseType getDatabaseType(){
-
return contextHolder.get();
-
}
-
}
定义动态数据源
-
public class DynamicDataSource extends AbstractRoutingDataSource{
-
-
protected Object determineCurrentLookupKey() {
-
return DatabaseContextHolder.getDatabaseType();
-
}
-
}
mybatis配置类
网上的很多文章配置出来都会产生数据源循环依赖的问题,这里解决了这个问题。
-
@Configuration
-
@MapperScan(basePackages="cn.test.jichi", sqlSessionFactoryRef="sessionFactory")
-
public class MybatisConfig {
-
-
/**
-
* @Description:设置动态数据源
-
*/
-
@Bean(name="dynamicDataSource")
-
@Primary
-
public DynamicDataSource DataSource(
-
@Qualifier("test1DataSource") DataSource test1DataSource,
-
@Qualifier("test2DataSource") DataSource test2DataSource,
-
@Qualifier("test3DataSource") DataSource test3DataSource){
-
Map<Object, Object> targetDataSource = new HashMap<>();
-
targetDataSource.put(DatabaseType.test1, test1DataSource);
-
targetDataSource.put(DatabaseType.test2, test2DataSource);
-
targetDataSource.put(DatabaseType.test3, test3DataSource);
-
DynamicDataSource dataSource = new DynamicDataSource();
-
dataSource.setTargetDataSources(targetDataSource);
-
dataSource.setDefaultTargetDataSource(test1DataSource);
-
return dataSource;
-
}
-
-
/**
-
* @Description:根据动态数据源创建sessionFactory
-
*/
-
@Bean(name="sessionFactory")
-
public SqlSessionFactory sessionFactory(
-
@Qualifier("test1DataSource") DataSource test1DataSource,
-
@Qualifier("test2DataSource") DataSource test2DataSource,
-
@Qualifier("test3DataSource") DataSource test3DataSource) throws Exception{
-
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
-
//构造方法,解决动态数据源循环依赖问题。
-
sessionFactoryBean.setDataSource(this.DataSource(test1DataSource,test2DataSource, test3DataSource));
-
return sessionFactoryBean.getObject();
-
}
-
}
-
package cn.chinaunicom.sdsi.utils;
-
-
import cn.chinaunicom.sdsi.config.dynamicDataSourceConfig.DatabaseContextHolder;
-
import cn.chinaunicom.sdsi.config.dynamicDataSourceConfig.DatabaseType;
-
-
/**
-
* @Description:封装数据源选择
-
*/
-
public class DynamicDataSourceUtils {
-
-
public static void chooseBasicDataSource(){
-
DatabaseContextHolder.setDatabaseType(DatabaseType.basic);
-
}
-
-
public static void chooseBranchDataSource(){
-
DatabaseContextHolder.setDatabaseType(DatabaseType.branch);
-
}
-
-
}
提供一个示例
-
public void testDymnaicDatasource(){
-
//不切换数据源默认是自己的。
-
System.out.println("-----默认数据源");
-
DemoEntity totalCount = demoMapper.getTotalCount();
-
String nameCount1 = totalCount.getNameCount();
-
String ageCount2 = totalCount.getAgeCount();
-
System.out.println("nameCount:"+nameCount1);
-
System.out.println("ageCount:"+ageCount2);
-
//数据源切换为branch
-
System.out.println("-----数据源为test2");
-
DynamicDataSourceUtils.chooseBranchDataSource();
-
Integer nameCount = demoMapper.getNameCount();
-
Integer ageCount = demoMapper.getAgeCount();
-
System.out.println("nameCount:"+nameCount);
-
System.out.println("ageCount:"+ageCount);
-
//数据源为basic
-
System.out.println("-----数据源为test3");
-
DynamicDataSourceUtils.chooseBasicDataSource();
-
Integer ageCount1 = demoMapper.getAgeCount();
-
System.out.println("ageCount:"+ageCount1);
-
-
}
总结
至此实现了多数据源的动态切换。可以在同一个方法里面进行操作多个数据源。
评论(0)