SpringBoot+Druid 运行时切换自定义数据源,前端页面切换数据源
1.功能说明
功能说明:运行中的程序,根据前端定义的ip和databaseName,不重启切换数据源,切换后调用其他接口不会再切回原来的数据源,永久切换。
2.application.yml 定义数据源和druid配置
注:启动类加上@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class})
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
dynamic:
primary: db1
datasource:
db1:
url: jdbc:sqlserver://localhost:1433;databaseName=aaa;useAffectedRows=true
username: admin
password: 123456
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
db2:
url: jdbc:sqlserver://192.168.2.01:1433;databaseName=bbb;useAffectedRows=true
username: admin
password: 123456
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
db3:
url: jdbc:sqlserver://192.168.2.02:1433;databaseName=ccc;useAffectedRows=true
username: admin
password: 123456
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: 1234567
# 初始化大小,最小,最大
initialSize: 5
minIdle: 10
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
#用来检测连接是否有效的sql 必须是一个查询语句
#mysql中为 select 'x'
#oracle中为 select 1 from dual
validationQuery: select 1
testWhileIdle: true
#申请连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
testOnBorrow: false
#归还连接时会执行validationQuery检测连接是否有效,开启会降低性能,默认为true
testOnReturn: false
#是否缓存preparedStatement,mysql5.5+建议开启
poolPreparedStatements: true
#当值大于0时poolPreparedStatements会自动修改为true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
# filters: stat,wall,log4j
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 2000
db-type: sqlserver
#开启log4jFilter
log4j2:
enabled: true
data-source-log-enabled: false
connection-log-enabled: false
statement-log-enabled: false
result-set-log-enabled: false
#开启WallFilter
wall:
enabled: true
db-type: sqlserver
log-violation: true
throw-exception: false
config:
delete-where-none-check: true
#通过connectProperties属性来打开mergeSql功能;慢SQL记录
#connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
web-stat-filter:
# 添加过滤规则
url-pattern: /*
# 忽略过滤格式
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
3.DataSourceConfig 定义默认数据源
@Slf4j
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.dynamic.datasource.db1.url}")
private String url;
@Value("${spring.datasource.dynamic.datasource.db1.username}")
private String username;
@Value("${spring.datasource.dynamic.datasource.db1.password}")
private String password;
@Value("${spring.datasource.dynamic.datasource.db1.driver-class-name}")
private String driverClassName;
@Bean
public DataSource defaultDataSource() {
DataSourceBuilder dsb = DataSourceBuilder.create();
dsb.url(this.url);
dsb.username(this.username);
dsb.password(this.password);
dsb.driverClassName(this.driverClassName);
return dsb.build();
}
@Bean
@Primary
@DependsOn({"springUtils", "defaultDataSource"})
public DynamicDataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(DynamicDataSource.dataSourcesMap);
return dynamicDataSource;
}
}
4.DynamicDataSource 定义数据源切换和重置方法
@Slf4j
@AutoConfigureAfter(DataSourceConfig.class)
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> dataSourceKey = ThreadLocal.withInitial(() -> "defaultDataSource");
public static Map<Object, Object> dataSourcesMap = new ConcurrentHashMap<>(10);
static {
dataSourcesMap.put("defaultDataSource", SpringUtils.getBean("defaultDataSource"));
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSource.dataSourceKey.get();
}
public static void setDataSource(String dataSource) {
DynamicDataSource.dataSourceKey.set(dataSource);
DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringUtils.getBean("dataSource");
dynamicDataSource.afterPropertiesSet();
}
public static String getDataSource() {
return DynamicDataSource.dataSourceKey.get();
}
public static void clear() {
DynamicDataSource.dataSourceKey.remove();
}
}
5.BaseServiceImpl 实现数据源切换和重置默认数据源
@Value("${spring.datasource.dynamic.datasource.db1.url}")
private String url;
@Value("${spring.datasource.dynamic.datasource.db1.username}")
private String username;
@Value("${spring.datasource.dynamic.datasource.db1.password}")
private String password;
@Value("${spring.datasource.dynamic.datasource.db1.driver-class-name}")
private String driverClassName;
@Override
public String changeDataSource(String ip, String dataBaseName) {
if(!this.pingIp(ip)){
return "ip地址连接失败!";
}
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:sqlserver://"+ip+":1433;databaseName="+dataBaseName+";useAffectedRows=true");
druidDataSource.setUsername(this.username);
druidDataSource.setPassword(this.password);
druidDataSource.setDriverClassName(this.driverClassName);
druidDataSource.setRemoveAbandoned(false);//true是否自动回收超时连接
// druidDataSource.setRemoveAbandonedTimeout(600);//超时时间(以秒数为单位)
druidDataSource.setLogAbandoned(true);//是否在自动回收超时连接的时候打印连接的超时错误
druidDataSource.setBreakAfterAcquireFailure(true);//true关闭重连
druidDataSource.setTimeBetweenConnectErrorMillis(60);//设置多久重试
druidDataSource.setConnectionErrorRetryAttempts(0);//重连次数,关闭重连需设为0
druidDataSource.setMaxWait(3000);// 超时等待时间以毫秒为单位
DynamicDataSource.dataSourcesMap.put("defaultDataSource", druidDataSource);
DynamicDataSource.setDataSource("defaultDataSource");//切换数据源
try {
DruidPooledConnection dpc = druidDataSource.getConnection();//关键!尝试连接,否则无法捕获druid报错
if(dpc!=null){
return "success";
}
} catch (SQLException e) {
log.error(e.getMessage());
this.resetDefaultDataBase();//连接失败后,重置默认数据源
}
return "数据库名称错误!";
}
@Override
public Boolean resetDefaultDataBase() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(this.url);
druidDataSource.setUsername(this.username);
druidDataSource.setPassword(this.password);
druidDataSource.setDriverClassName(this.driverClassName);
druidDataSource.setRemoveAbandoned(false);
druidDataSource.setLogAbandoned(true);
druidDataSource.setBreakAfterAcquireFailure(true);
druidDataSource.setTimeBetweenConnectErrorMillis(60);
druidDataSource.setConnectionErrorRetryAttempts(0);
druidDataSource.setMaxWait(3000);
DynamicDataSource.dataSourcesMap.put("defaultDataSource", druidDataSource);
DynamicDataSource.setDataSource("defaultDataSource");//切换数据源
try {
DruidPooledConnection dpc = druidDataSource.getConnection();
if(dpc!=null){
return true;
}
} catch (SQLException e) {
...
}
return false;
}
水平有限,有错误之处或者有好的方法欢迎指点!