开始前的准备
添加依赖
<!--aop支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
多数据源配置文件
用于存放数据库的配置信息
public class DataSourceContainer {
private String username;
private String password;
private String url;
private String driverClassName;
/**
* @info 记录数据源的名称,为了切换数据源做准备
*/
private String name;
/**
* @info 描述数据库
*/
private String describe;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
@Override
public String toString() {
return "DataSourceContainer [username=" + username + ", password=" + password + ", url=" + url
+ ", driverClassName=" + driverClassName + ", name=" + name + ", describe=" + describe + "]";
}
}
可以在yml或者properties中使用提示配置
@Configuration
@ConfigurationProperties(prefix = DataSourceList.prefix)
public class DataSourceList {
protected static final String prefix = "project.data-source-list";
private List<DataSourceContainer> dataSourceList;
public List<DataSourceContainer> getDataSourceList() {
return dataSourceList;
}
public void setDataSourceList(List<DataSourceContainer> dataSourceList) {
this.dataSourceList = dataSourceList;
}
@Override
public String toString() {
return "DataSourceList [dataSourceList=" + dataSourceList + "]";
}
}
yml 配置
# 项目多数据源配置
project:
data-source-list:
data-source-list:
-
username:
password:
url:
describe:
name:
driver-class-name:
-
username:
password:
url:
describe:
name:
driver-class-name:
创建可根据key名称切换的动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
public static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
DataSourceEnum key = DataSourceContextHolder.get();
logger.info("当前数据源{}", key == null ? DataSourceEnum.defaultDataSource : key);
return key;
}
}
核心:动态数据源配置,将所有数据源创建出来,并交给spring负责管理
@Configuration
public class DynamicDataSourceConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSourceConfiguration.class);
// 数据源名称集合
private static List<String> dataSourceNamelist = new ArrayList<>();
// 其他数据源集合 key - 名称, value 。。。。
private static Map<Object, Object> targetDataSourceMap = new HashMap<>();
private static void notTrue(boolean bo, String message) {
if (bo == false) {
throw new IllegalArgumentException(message);
}
}
public DynamicDataSourceConfiguration(@Autowired DataSourceList dataSourceList) {
List<DataSourceContainer> daList = dataSourceList.getDataSourceList();
Assert.notNull(daList, "请配置更多的数据源");
if (LOGGER.isInfoEnabled()) {
LOGGER.info("项目中数据源包括{}", dataSourceList);
}
DataSourceEnum[] values = DataSourceEnum.values();
for (DataSourceEnum val : values) {
dataSourceNamelist.add(val.name());
}
for (DataSourceContainer dc : daList) {
String username = dc.getUsername();
String password = dc.getPassword();
String url = dc.getUrl();
String name = dc.getName();
notTrue(dataSourceNamelist.contains(name),
"请确定你的数据源实例名称和" + DataSourceEnum.class.getName() + "类中任意一个元素匹配。");
DruidDataSource build = DruidDataSourceBuilder.create().build();
build.setUsername(username);
build.setPassword(password);
build.setUrl(url);
targetDataSourceMap.put(DataSourceEnum.valueOf(name), build);
}
}
/**
* @info 默认数据库
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource defaultDataSource() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("在{}创建了默认数据源", DateUtil.getCurrentTimeStr(DateConstant.DEFAULT));
}
return DruidDataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setDefaultTargetDataSource(defaultDataSource());
targetDataSourceMap.remove(DataSourceEnum.defaultDataSource);
targetDataSourceMap.put(DataSourceEnum.defaultDataSource, defaultDataSource());
dataSource.setTargetDataSources(targetDataSourceMap);
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
// 设置别名
sqlSessionFactoryBean.setTypeAliasesPackage("com.sinosoft.model.dbmodel");
// 此处设置为了解决找不到mapper文件的问题
sqlSessionFactoryBean
.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplate() throws Exception {
return new SqlSessionTemplate(sqlSessionFactory());
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dynamicDataSource());
}
/**
* @info 事务管理,多个数据库都可以使用
* @return 事务管理实例
*/
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
如何使用
根据我们继承AbstractRoutingDataSource类重写determineCurrentLookupKey方法的返回值的类型 进行key切换