今天在看源码DataSource同目录下的类的时候留意到DruidDataSourceC3P0Adapter和DruidDataSourceC3P0AdapterMBean这两个类,发现在前面看的源码中没有关于这块的调用,随即就开始研究一下具体用来做什么的?
//构造方法
public DruidDataSourceC3P0Adapter(){
//注入数据源
dataSource = new DruidDataSource();
// 设置默认的初始化大小为3
dataSource.setInitialSize(3);
//acquireincrement表示当我们数据库连接池中没有空闲的连接时,它一次性创建的连接数量,我们设置几就一次性创建几个连接,你也可以理解为数据库中的连接都被使用了,没有了连接的时候或者是连接耗尽的时候我们可以一次性的创建n连接,这个n的值由该acquireincrement属性来决定。
acquireIncrement = 3;
//连接异常的时间间隔
dataSource.setTimeBetweenConnectErrorMillis(1000);
//自动提交
dataSource.setDefaultAutoCommit(true);
//logAbandoned=false,表示在回收连接的时候,不在日志中输出回收的连接的信息,包括是在哪用的这个连接,可以用来追踪连接溢出。
dataSource.setLogAbandoned(false);
dataSource.setMaxActive(15);
dataSource.setMinIdle(3);
//testOnBorrow、testOnReturn这些参数设置为true时,分别表示在获取连接时、归还连接时、空闲时检查连接的可用性,如果发生断开的情况则会重连。这边设置为false
dataSource.setTestOnReturn(false);
dataSource.setTestOnBorrow(false);
}
//全局看了相关的源码 发现只有这ComboPooledDataSource调用了DruidDataSourceC3P0Adapter,但是却没有具体的实现,所以不太清楚是否还有别的作用。
public class ComboPooledDataSource extends DruidDataSourceC3P0Adapter implements PooledDataSource {
}
看过druid的源码就会发现,相比其他DBCP和C3P0,druid有以下特点:
CountDownLatch
、ReentrantLock
、AtomicLongFieldUpdater
、Condition
等,也就是说,在分析druid源码之前,最好先学习下这些技术;DruidDataSource
里面。另外,在对类或接口的抽象上,个人感觉,druid不是很“面向对象”,有的接口或类的方法很难统一成某种对象的行为,所以,本文不会去关注类的设计,更多地将分析一些重要功能的实现。校验连接,根据数据库方言不同,实现不同的校验方案。ValidConnectionChecker在DruidDataSource核心代码模块进行调用,具体相关的子类的类图如下。
//DruidDataSource 中的校验连接
public void validateConnection(Connection conn) throws SQLException {
String query = getValidationQuery();
//如果已关闭 抛出异常
if (conn.isClosed()) {
throw new SQLException("validateConnection: connection closed");
}
if (validConnectionChecker != null) {
// 数据库方言对应的check不为空
boolean result;
Exception error = null;
try {
// 是否是有效连接。这个代码具体看下子类的实现
result = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
if (result && onFatalError) {
//加锁 // 这块为什么要加锁呢??? 只是判断一个属性
lock.lock();
try {
if (onFatalError) {
onFatalError = false;
}
} finally {
lock.unlock();
}
}
} catch (SQLException ex) {
throw ex;
} catch (Exception ex) {
result = false;
error = ex;
}
if (!result) {
SQLException sqlError = error != null ? //
new SQLException("validateConnection false", error) //
: new SQLException("validateConnection false");
throw sqlError;
}
return;
}
if (null != query) {
//查询不为空
Statement stmt = null;
ResultSet rs = null;
try {
//创建 statement
stmt = conn.createStatement();
if (getValidationQueryTimeout() > 0) {
//超时时间
stmt.setQueryTimeout(getValidationQueryTimeout());
}
//执行sql
rs = stmt.executeQuery(query);
if (!rs.next()) {
throw new SQLException("validationQuery didn't return a row");
}
if (onFatalError) {
//致命异常
lock.lock();
try {
if (onFatalError) {
onFatalError = false;
}
}
finally {
lock.unlock();
}
}
} finally {
//关闭
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
}
@Override
//校验连接
public boolean isValidConnection(Connection conn, String query, int validationQueryTimeout) throws Exception {
if (query == null || query.length() == 0) {
return true;
}
Statement stmt = null;
ResultSet rs = null;
try {
//创建stament
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
//超时时间
stmt.setQueryTimeout(validationQueryTimeout);
}
//执行sql
rs = stmt.executeQuery(query);
//返回为true
return true;
} finally {
//关闭
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
MySqlValidConnectionChecker
//mysql 数据库校验连接的方式竟然通过ping的方式
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
if (conn.isClosed()) {
return false;
}
if (usePingMethod) {
//获取连接
if (conn instanceof DruidPooledConnection) {
conn = ((DruidPooledConnection) conn).getConnection();
}
if (conn instanceof ConnectionProxy) {
conn = ((ConnectionProxy) conn).getRawObject();
}
if (clazz.isAssignableFrom(conn.getClass())) {
if (validationQueryTimeout <= 0) {
validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
}
try {
// ping连接
ping.invoke(conn, true, validationQueryTimeout * 1000);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof SQLException) {
throw (SQLException) cause;
}
throw e;
}
return true;
}
}
String query = validateQuery;
if (validateQuery == null || validateQuery.isEmpty()) {
query = DEFAULT_VALIDATION_QUERY;
}
Statement stmt = null;
ResultSet rs = null;
try {
//创建statement
stmt = conn.createStatement();
if (validationQueryTimeout > 0) {
stmt.setQueryTimeout(validationQueryTimeout);
}
//执行查询
rs = stmt.executeQuery(query);
return true;
} finally {
JdbcUtils.close(rs);
JdbcUtils.close(stmt);
}
}
当Druid遇到testWhileIdle,testOnBorrow,testOnReturn时,就会验证连接的有效性,验证规则如下:
如果有相关数据库的ValidConnectionChecker,则使用ValidConnectionChecker验证(Druid提供常用数据库的ValidConnectionChecker,包括MSSQLValidConnectionChecker,MySqlValidConnectionChecker,OracleValidConnectionChecker,PGValidConnectionChecker);
如果没有ValidConnectionChecker,则直接使用validationQuery验证;MySqlValidConnectionChecker会使用Mysql独有的ping方式进行验证,其他数据库其实也都是使用validationQuery进行验证
今天把整体的大流程又过了一下,发现有一些之前没有关注到的细节点,也发现作者定义了一些尚未实现的方法,这些应该是为了以后拓展使用吧。后面整体输出一张脑图,把整个流程串起来。