<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${dynamic.version}</version>
</dependency>
简介
前两篇博客介绍了用基本的方式做多数据源,可以应对一般的情况,但是遇到一些复杂的情况就需要扩展下功能了,比如:动态增减数据源、数据源分组,纯粹多库 读写分离 一主多从、从其他数据库或者配置中心读取数据源等等。其实就算没有这些需求,使用这个实现多数据源也比之前使用AbstractRoutingDataSource要便捷的多
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
github: https://github.com/baomidou/dynamic-datasource-spring-boot-starter
文档: https://github.com/baomidou/dynamic-datasource-spring-boot-starter/wiki
它跟mybatis-plus是一个生态圈里的,很容易集成mybatis-plus
特性:
数据源分组,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
内置敏感参数加密和启动初始化表结构schema数据库database。
提供对Druid,Mybatis-Plus,P6sy,Jndi的快速集成。
简化Druid和HikariCp配置,提供全局参数配置。
提供自定义数据源来源接口(默认使用yml或properties配置)。
提供项目启动后增减数据源方案。
提供Mybatis环境下的 纯读写分离 方案。
使用spel动态参数解析数据源,如从session,header或参数中获取数据源。(多租户架构神器)
提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个Service都是不同的数据源)
提供 不使用注解 而 使用 正则 或 spel 来切换数据源方案(实验性功能)。
基于seata的分布式事务支持。
==============================================================================================================
核心配置类
DynamicDataSourceAutoConfiguration
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); // 改造时自定义, 并覆盖核心方法
dataSource.setPrimary(properties.getPrimary());
dataSource.setStrategy(properties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(properties.getP6spy());
dataSource.setStrict(properties.getStrict());
return dataSource;
}
抽象动态获取数据源
AbstractRoutingDataSource
/**
* 子类实现决定最终数据源
*
* @return 数据源
*/
protected abstract DataSource determineDataSource();
@Override
public Connection getConnection() throws SQLException {
return determineDataSource().getConnection();
}
核心动态数据源组件
DynamicRoutingDataSource
/**
* 所有数据库
*/
private Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();
/**
* 分组数据库
*/
private Map<String, DynamicGroupDataSource> groupDataSources = new ConcurrentHashMap<>();
@Override
public DataSource determineDataSource() {
return getDataSource(DynamicDataSourceContextHolder.peek());
}
private DataSource determinePrimaryDataSource() {
log.debug("从默认数据源中返回数据");
return groupDataSources.containsKey(primary) ? groupDataSources.get(primary).determineDataSource() : dataSourceMap.get(primary);
}
/**
* 获取当前所有的数据源
*
* @return 当前所有数据源
*/
public Map<String, DataSource> getCurrentDataSources() {
return dataSourceMap;
}
/**
* 获取的当前所有的分组数据源
*
* @return 当前所有的分组数据源
*/
public Map<String, DynamicGroupDataSource> getCurrentGroupDataSources() {
return groupDataSources;
}
/**
* 获取数据源
*
* @param ds 数据源名称
* @return 数据源
*/
public DataSource getDataSource(String ds) {
if (StringUtils.isEmpty(ds)) {
return determinePrimaryDataSource();
} else if (!groupDataSources.isEmpty() && groupDataSources.containsKey(ds)) {
log.debug("从 {} 组数据源中返回数据源", ds);
return groupDataSources.get(ds).determineDataSource();
} else if (dataSourceMap.containsKey(ds)) {
log.debug("从 {} 单数据源中返回数据源", ds);
return dataSourceMap.get(ds);
}
if (strict) {
throw new RuntimeException("不能找到名称为" + ds + "的数据源");
}
return determinePrimaryDataSource();
}
===================================================================================================
如果有特殊业务, 例如多数据源事务控制(单个节点(分布式事务另当别论)), 可以参考以上核心代码加以改造, 简记....................以防忘记!!!!!!!!!