SpringBoot学习笔记:动态数据源切换
数据源
Java的javax.sql.DataSource接口提供了一种处理数据库链接的标准方法。一般,DataSource使用URL和一些凭据来创建数据库链接。html
SpringBoot默认提供了针对内存数据库的数据源,如H二、hqldb、Derby等。java
配置数据源信息
当咱们引入spring-boot-start-jdbc时,SpringBoot会默认使用其绑定的Tomcat-JDBC的数据源。
mysql
DataSource可由spring.datasource.*中的外部配置属性控制。例如,您能够在application.properties中声明如下部分:spring
spring.datasource.url=jdbc:mysql://localhost/test spring.datasource.username=dbuser spring.datasource.password=dbpass spring.datasource.driver-class-name=com.mysql.jdbc.Driver
固然,咱们也能够是使用基于Java代码的配置方法:sql
@Bean public DataSource mysqlDataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/demo"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; }
使用第三方数据源
有时候,咱们须要使用第三方的数据源,如Druid、C3P0等,在SpringBoot中也很简单。数据库
以Druid为例,首先添加Maven依赖:安全
<dependencies> <!-- 添加MySQL依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 添加JDBC依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!-- 添加Druid依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> </dependencies>
配置数据库链接信息便可:bash
@Configuration public class DataSourceConfig { @Autowired private Environment env; @Bean public DataSource getDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(env.getProperty("spring.datasource.url")); dataSource.setUsername(env.getProperty("spring.datasource.username")); dataSource.setPassword(env.getProperty("spring.datasource.password")); return dataSource; } }
动态新增数据源
有时候,咱们须要在程序运行过程当中动态增长或改变数据源。app
改变数据源
Spring2.0 引入了AbstractRoutingDataSource,该类能够充当DataSource的路由中介,能在运行时,动态切换当前数据源。以下为其核心代码,可见他是根据determineCurrentLookupKey()获取Key值,来从resolvedDataSources中找到要切换的数据源。ide
private Map<Object, Object> targetDataSources; private Map<Object, DataSource> resolvedDataSources; protected DataSource determineTargetDataSource() { Assert.notNull(this.resolvedDataSources, "DataSource router not initialized"); Object lookupKey = this.determineCurrentLookupKey(); DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey); if (dataSource == null && (this.lenientFallback || lookupKey == null)) { dataSource = this.resolvedDefaultDataSource; } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } else { return dataSource; } }
而这个determineCurrentLookupKey函数须要咱们自已来实现,因此咱们要定义一个子类DynamicRoutingDataSource来继承AbstractRoutingDataSource,而后实现这个方法,其实就是返回一个Key字符串!。
为了保证线程安全,咱们能够把数据源的Key信息保存在ThreadLocal中,以下:
public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() { @Override protected String initialValue() { return "dynamic_db0"; } }; /** * To switch DataSource * * @param key the key */ public static void setDataSourceKey(String key) { contextHolder.set(key); } /** * Get current DataSource * * @return data source key */ public static String getDataSourceKey() { return contextHolder.get(); } /** * To set DataSource as default */ public static void clearDataSourceKey() { contextHolder.remove(); } }
这样,determineCurrentLookupKey就能够以下定义:
@Override protected Object determineCurrentLookupKey() { System.out.println("Current DataSource is [{}]"+ DynamicDataSourceContextHolder.getDataSourceKey()); return DynamicDataSourceContextHolder.getDataSourceKey(); }
因此咱们在使用时,只须要使用DynamicDataSourceContextHolder.setDataSourceKey(),并能够切换数据源。
新增数据源
新增数据源,其实就是更新targetDataSources字段,一个简单的Demo以下:
/** * 动态增长数据源 * * @param map 数据源属性 * @return */ public synchronized boolean addDataSource(Map<String, String> map) { try { String database = map.get("database");//获取要添加的数据库名 if (database==null||database.equals("")) return false; if (DynamicRoutingDataSource.isExistDataSource(database)) return true; DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl(map.get("url")); dataSource.setUsername(map.get("username")); dataSource.setPassword(map.get("password")); Map<Object, Object> targetMap = DynamicRoutingDataSource.targetDataSources; targetMap.put(database, dataSource); // 当前 targetDataSources 与 父类 targetDataSources 为同一对象 因此不须要set // this.setTargetDataSources(targetMap); this.afterPropertiesSet(); } catch (Exception e) { logger.error(e.getMessage()); return false; } return true; }
配置动态数据源
咱们能够将其以Bean形式配置,并在其中设置默认数据源。
@Bean("dynamicDataSource") public DynamicRoutingDataSource dynamicDataSource() { DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); Map<Object, Object> dataSourceMap = new HashMap<>(); DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/demo"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSourceMap.put("DEMO", dataSource); dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource);// 设置默认数据源 dynamicRoutingDataSource.setTargetDataSources(dataSourceMap); return dynamicRoutingDataSource; }
以后,咱们在代码中即可以执行新增数据源、切换数据源操做等等。
参考文档: