赞
踩
通过实现Spring提供的AbstractRoutingDataSource类,可以实现自己的数据源选择逻辑,从而可以实现数据源的动态切换。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Value("${spring.datasource.default-db-key}")
private String defaultDbKey;·
@Override
protected Object determineCurrentLookupKey() {
String currentDb = DynamicDataSourceService.currentDb();
if (currentDb == null) {
return defaultDbKey;
}
return currentDb;
}
}
跟配置静态多数据源一样,需要手动配置下面的三个 Bean,只不过DynamicDataSource类的targetDataSources是空的。
@Configuration public class DynamicDataSourceConfig { /** * 动态数据源 */ @Bean public DynamicDataSource dynamicDataSource() { DynamicDataSource dataSource = new DynamicDataSource(); Map<Object, Object> targetDataSources = new HashMap<>(); dataSource.setTargetDataSources(targetDataSources); return dataSource; } /** * 会话工厂 */ @Bean public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException { org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dynamicDataSource()); sqlSessionFactoryBean.setConfiguration(configuration); PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/repository/*.xml")); return sqlSessionFactoryBean; } /** * 事务管理器 */ @Bean public PlatformTransactionManager transactionManager() { return new DataSourceTransactionManager(dynamicDataSource()); } }
这是一个比较核心的工具类,提供了一些静态方法从而可以实现一些功能,包括:
动态添加数据源、切换数据源、重置数据源、获取数据源。
在 DynamicDataSource 类中,就是调用了 DynamicDataSourceService 类的 switchDb
方法实现的数据源选择。
通过查看下面的代码就能看出来使用线程本地的技术实现的多个请求数据源互不相干。
@Slf4j public class DynamicDataSourceService { private static final Map<Object, Object> dataSources = new HashMap<>(); private static final ThreadLocal<String> dbKeys = ThreadLocal.withInitial(() -> null); /** * 动态添加一个数据源 * * @param name 数据源的key * @param dataSource 数据源对象 */ public static void addDataSource(String name, DataSource dataSource) { DynamicDataSource dynamicDataSource = App.context.getBean(DynamicDataSource.class); dataSources.put(name, dataSource); dynamicDataSource.setTargetDataSources(dataSources); dynamicDataSource.afterPropertiesSet(); log.info("addDataSource 添加了数据源:{}", name); } /** * 切换数据源 */ public static void switchDb(String dbKey) { dbKeys.set(dbKey); log.info("switchDb 切换数据源:{}", dbKey); } /** * 重置数据源 */ public static void resetDb() { dbKeys.remove(); log.info("resetDb 重置数据源"); } /** * 获取当前数据源 */ public static String currentDb() { log.info("currentDb 获取当前数据源"); return dbKeys.get(); } }
至此,动态多数据源的配置就完成了,只需要编写数据源生成的逻辑,在程序运行时调用 addDataSource 方法即可将数据源动态添加到上下文中,并支持动态切换。
下面简单介绍一下基于配置文件的数据源生成。
public interface DataSourceProvider {
List<DataSource> provide();
}
@Component @ConfigurationProperties(prefix = "spring.datasource.hikari") public class YmlDataSourceProvider implements DataSourceProvider { private List<Map<String, DataSourceProperties>> dataSources; private DataSource buildDataSource(DataSourceProperties prop) { DataSourceBuilder<?> builder = DataSourceBuilder.create(); builder.driverClassName(prop.getDriverClassName()); builder.username(prop.getUsername()); builder.password(prop.getPassword()); builder.url(prop.getJdbcUrl()); return builder.build(); } @Override public List<DataSource> provide() { List<DataSource> res = new ArrayList<>(); dataSources.forEach(map -> { Set<String> keys = map.keySet(); keys.forEach(key -> { DataSourceProperties properties = map.get(key); DataSource dataSource = buildDataSource(properties); DynamicDataSourceService.addDataSource(key, dataSource); }); }); return res; } @PostConstruct public void init() { provide(); } public List<Map<String, DataSourceProperties>> getDataSources() { return dataSources; } public void setDataSources(List<Map<String, DataSourceProperties>> dataSources) { this.dataSources = dataSources; } }
看一下对应的配置文件内容:
spring:
datasource:
default-db-key: db0
hikari:
data-sources:
- db0:
jdbc-url: jdbc:mysql://127.0.0.1:3306/codingcea
username: codingce
password: 1234567890
driver-class-name: com.mysql.cj.jdbc.Driver
- db1:
jdbc-url: jdbc:mysql://127.0.0.1:3306/codingceb
username: codingce
password: 1234567890
driver-class-name: com.mysql.cj.jdbc.Driver
这样就实现了应用启动时自动将配置文件中的数据源配置读取并生成数据源注册到上下文中;
当然也可以有其他的实现,比如从数据库读取并配置,或者通过接口请求的方式生成都可以,只要实现自己的DataSourceProvider就可以。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。