|
@@ -3,6 +3,7 @@ package com.uas.kanban.support;
|
|
|
import java.sql.SQLException;
|
|
import java.sql.SQLException;
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
@@ -28,44 +29,134 @@ public class DataSourceManager {
|
|
|
private BaseDao<DataSource> dataSourceDao;
|
|
private BaseDao<DataSource> dataSourceDao;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 以数据源 code 为键,缓存 DruidDataSource 数据库连接,避免重复建立 DruidDataSource
|
|
|
|
|
|
|
+ * 以数据源 code 为键,缓存数据库连接,避免重复建立连接
|
|
|
*/
|
|
*/
|
|
|
- private Map<String, DruidDataSource> dataSources = new HashMap<>();
|
|
|
|
|
|
|
+ private Map<String, DataSourceCache> dataSourceCaches = new HashMap<>();
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 根据数据源 {@link DataSource} 的 code 获取 DruidDataSource
|
|
|
|
|
|
|
+ * 根据数据源 {@link DataSource} 的 code 获取 NewbieJdbc 对象
|
|
|
*
|
|
*
|
|
|
* @param dataSourceCode
|
|
* @param dataSourceCode
|
|
|
* 数据源 code
|
|
* 数据源 code
|
|
|
- * @return DruidDataSource 对象
|
|
|
|
|
|
|
+ * @return NewbieJdbc 对象
|
|
|
* @throws SQLException
|
|
* @throws SQLException
|
|
|
*/
|
|
*/
|
|
|
- public DruidDataSource getDataSource(@NotEmpty("dataSourceCode") String dataSourceCode) throws SQLException {
|
|
|
|
|
- DruidDataSource druidDataSource = dataSources.get(dataSourceCode);
|
|
|
|
|
- if (druidDataSource != null) {
|
|
|
|
|
- return druidDataSource;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public NewbieJdbc getJdbc(@NotEmpty("dataSourceCode") String dataSourceCode) throws SQLException {
|
|
|
|
|
+ return getCache(dataSourceCode).getJdbc();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据数据源 {@link DataSource} 的 code 获取数据库连接缓存
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param dataSourceCode
|
|
|
|
|
+ * 数据源 code
|
|
|
|
|
+ * @return 数据库连接缓存
|
|
|
|
|
+ * @throws SQLException
|
|
|
|
|
+ */
|
|
|
|
|
+ private DataSourceCache getCache(@NotEmpty("dataSourceCode") String dataSourceCode) throws SQLException {
|
|
|
|
|
+ DataSourceCache dataSourceCache = dataSourceCaches.get(dataSourceCode);
|
|
|
DataSource dataSource = dataSourceDao.findOne(dataSourceCode);
|
|
DataSource dataSource = dataSourceDao.findOne(dataSourceCode);
|
|
|
if (dataSource == null) {
|
|
if (dataSource == null) {
|
|
|
throw new IllegalStateException("数据源不存在:" + dataSourceCode);
|
|
throw new IllegalStateException("数据源不存在:" + dataSourceCode);
|
|
|
}
|
|
}
|
|
|
dataSourceDao.checkFields(dataSource);
|
|
dataSourceDao.checkFields(dataSource);
|
|
|
- druidDataSource = dataSource.toDruidDataSource();
|
|
|
|
|
- dataSources.put(dataSourceCode, druidDataSource);
|
|
|
|
|
- return druidDataSource;
|
|
|
|
|
|
|
+ // 存在数据库连接缓存
|
|
|
|
|
+ if (dataSourceCache != null) {
|
|
|
|
|
+ // 数据源信息没有更改,则使用缓存
|
|
|
|
|
+ if (Objects.equals(dataSourceCache.getDataSource(), dataSource)) {
|
|
|
|
|
+ return dataSourceCache;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 否则先清除旧的连接,再新建连接
|
|
|
|
|
+ dataSourceCache.getDruidDataSource().close();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ dataSourceCache = new DataSourceCache(dataSource);
|
|
|
|
|
+ dataSourceCaches.put(dataSourceCode, dataSourceCache);
|
|
|
|
|
+ return dataSourceCache;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 根据数据源 {@link DataSource} 的 code 获取 NewbieJdbc 对象
|
|
|
|
|
|
|
+ * 数据库连接缓存
|
|
|
*
|
|
*
|
|
|
- * @param dataSourceCode
|
|
|
|
|
- * 数据源 code
|
|
|
|
|
- * @return NewbieJdbc 对象
|
|
|
|
|
- * @throws SQLException
|
|
|
|
|
|
|
+ * @author sunyj
|
|
|
|
|
+ * @since 2017年9月15日 下午9:12:32
|
|
|
*/
|
|
*/
|
|
|
- public NewbieJdbc getJdbc(@NotEmpty("dataSourceCode") String dataSourceCode) throws SQLException {
|
|
|
|
|
- DruidDataSource druidDataSource = getDataSource(dataSourceCode);
|
|
|
|
|
- return new NewbieJdbcSupport(druidDataSource);
|
|
|
|
|
|
|
+ private class DataSourceCache {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 数据源信息
|
|
|
|
|
+ */
|
|
|
|
|
+ private DataSource dataSource;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 数据库连接
|
|
|
|
|
+ */
|
|
|
|
|
+ private DruidDataSource druidDataSource;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * jdbc
|
|
|
|
|
+ */
|
|
|
|
|
+ private NewbieJdbcSupport jdbc;
|
|
|
|
|
+
|
|
|
|
|
+ public DataSourceCache(@NotEmpty("dataSource") DataSource dataSource) {
|
|
|
|
|
+ this.dataSource = dataSource;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public DataSource getDataSource() {
|
|
|
|
|
+ return dataSource;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public DruidDataSource getDruidDataSource() throws SQLException {
|
|
|
|
|
+ if (druidDataSource == null) {
|
|
|
|
|
+ druidDataSource = toDruidDataSource();
|
|
|
|
|
+ // 如果在 toDruidDataSource 方法里 init,出现异常的话,druidDataSource 会一直为
|
|
|
|
|
+ // null,这样会不断创建连接,因此先使 druidDataSource 不为 null ,再 init
|
|
|
|
|
+ druidDataSource.init();
|
|
|
|
|
+ }
|
|
|
|
|
+ return druidDataSource;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public NewbieJdbcSupport getJdbc() throws SQLException {
|
|
|
|
|
+ if (jdbc == null) {
|
|
|
|
|
+ jdbc = new NewbieJdbcSupport(getDruidDataSource());
|
|
|
|
|
+ }
|
|
|
|
|
+ return jdbc;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private DruidDataSource toDruidDataSource() throws SQLException {
|
|
|
|
|
+ DruidDataSource ds = new DruidDataSource();
|
|
|
|
|
+ ds.setDriverClassName(dataSource.getDriverClassName());
|
|
|
|
|
+ ds.setUrl(dataSource.getUrl());
|
|
|
|
|
+ ds.setUsername(dataSource.getUsername());
|
|
|
|
|
+ ds.setPassword(dataSource.getPassword());
|
|
|
|
|
+ // 配置初始化大小、最小、最大
|
|
|
|
|
+ ds.setInitialSize(1);
|
|
|
|
|
+ ds.setMinIdle(1);
|
|
|
|
|
+ ds.setMaxActive(10);
|
|
|
|
|
+ // 配置获取连接等待超时的时间
|
|
|
|
|
+ ds.setMaxWait(60000);
|
|
|
|
|
+ // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
|
|
|
|
+ ds.setTimeBetweenEvictionRunsMillis(6000);
|
|
|
|
|
+ // 配置一个连接在池中最小生存的时间,单位是毫秒
|
|
|
|
|
+ ds.setMinEvictableIdleTimeMillis(300000);
|
|
|
|
|
+ ds.setValidationQuery("SELECT 1 FROM DUAL");
|
|
|
|
|
+ ds.setTestWhileIdle(true);
|
|
|
|
|
+ ds.setTestOnBorrow(true);
|
|
|
|
|
+ ds.setTestOnReturn(false);
|
|
|
|
|
+ // 连接泄漏处理,Druid提供了RemoveAbandanded相关配置,用来关闭长时间不使用的连接(例如忘记关闭连接)
|
|
|
|
|
+ ds.setRemoveAbandoned(true);
|
|
|
|
|
+ ds.setRemoveAbandonedTimeout(1800);
|
|
|
|
|
+ // 关闭abanded连接时输出错误日志
|
|
|
|
|
+ ds.setLogAbandoned(true);
|
|
|
|
|
+ // 打开PSCache,并且指定每个连接上PSCache的大小
|
|
|
|
|
+ ds.setPoolPreparedStatements(true);
|
|
|
|
|
+ ds.setMaxPoolPreparedStatementPerConnectionSize(20);
|
|
|
|
|
+ ds.setTimeBetweenLogStatsMillis(60000);
|
|
|
|
|
+ // 配置监控统计拦截的filters, 监控统计:"stat",防SQL注入:"wall",组合使用: "stat,wall"
|
|
|
|
|
+ ds.setFilters("stat,slf4j,wall");
|
|
|
|
|
+ ds.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");
|
|
|
|
|
+ return ds;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|