1.检查连接状态
在Druid执行一条SQL预编译之前,将会通过内部状态检查当前连接状态,如果当前连接状态不可用(连接处于closed、disable、或者不被连接池所持有),将不会继续进行SQL预编译
public void checkState() throws SQLException {
final boolean asyncCloseEnabled;
if (holder != null) {
//从连接池中取出对应状态配置,否则默认为false
asyncCloseEnabled = holder.getDataSource().isAsyncCloseConnectionEnable();
} else {
asyncCloseEnabled = false;
}
//通过内部状态检查连接是否被关闭或处于关闭中
if (asyncCloseEnabled) {
//如果开起了异步关闭连接选项,对当前连接加锁处理
lock.lock();
try {
checkStateInternal();
} finally {
lock.unlock();
}
} else {
checkStateInternal();
}
}
/**
* 通过内部状态检查数据库连接,如果内部状态存在异常,将抛出异常
* @throws SQLException
*/
private void checkStateInternal() throws SQLException {
if (closed) {
if (disableError != null) {
throw new SQLException("connection closed", disableError);
} else {
throw new SQLException("connection closed");
}
}
if (disable) {
if (disableError != null) {
throw new SQLException("connection disabled", disableError);
} else {
throw new SQLException("connection disabled");
}
}
if (holder == null) {
if (disableError != null) {
throw new SQLException("connection holder is null", disableError);
} else {
throw new SQLException("connection holder is null");
}
}
}
2.获得预编译SQL持有者
在这一步将生成SQL持有者对象,在这里可以通过开启缓存预编译SQL参数来缓存预编译SQL对象
PreparedStatementHolder stmtHolder = null;
//创建预编译缓存对象
PreparedStatementKey key = new PreparedStatementKey(sql, getCatalog(), MethodType.M1);
//判断是否开启了缓存预编译SQL功能
boolean poolPreparedStatements = holder.isPoolPreparedStatements();
//如果开启了该功能,则在PreparedStatementPool获得预编译对象持有者
if (poolPreparedStatements) {
stmtHolder = holder.getStatementPool().get(key);
}
//如果该SQL没有经过预编译或没有开启缓存预编译参数,对SQL进行预编译处理
if (stmtHolder == null) {
try {
//生成预编译SQL持有者
stmtHolder = new PreparedStatementHolder(key, conn.prepareStatement(sql));
//记录预编译SQL数
holder.getDataSource().incrementPreparedStatementCount();
} catch (SQLException ex) {
//对异常进行处理,此处将抛出SQLException异常
handleException(ex, sql);
}
}
2.1 生成新的预编译SQL对象
在生成新的PreparedStatementHolder
对象时,可以看到调用了SQL预编译的方法进行预编译SQL,该连接对象实际持有的是通过DruidAbstractDataSource.createPhysicalConnection
方法所创建的连接对象,此处使用过滤器时使用连接代理的目的是为了便于过滤器对相关指标进行统计。
/**
* 创建物理连接
* @param url
* @param info
* @return
* @throws SQLException
*/
public Connection createPhysicalConnection(String url, Properties info) throws SQLException {
Connection conn;
if (getProxyFilters().isEmpty()) {
//如果Druid没有配置过滤器,则使用驱动默认的连接
conn = getDriver().connect(url, info);
} else {
//否则使用包装后的连接对象,即ConnectionProxy
conn = new FilterChainImpl(this).connection_connect(info);
}
createCountUpdater.incrementAndGet(this);
return conn;
}
2.2 根据过滤链建立链接
public ConnectionProxy connection_connect(Properties info) throws SQLException {
//如果当前过滤练不是最后一个,则向下执行过滤
if (this.pos < filterSize) {
return nextFilter()
.connection_connect(this, info);
}
//当过滤链到达最后一位时,建立真实连接
//得到数据库原始驱动以及数据库连接URL
Driver driver = dataSource.getRawDriver();
String url = dataSource.getRawJdbcUrl();
//利用驱动自身实现,建立数据库连接
Connection nativeConnection = driver.connect(url, info);
//当数据库连接建立失败是,返回空值
if (nativeConnection == null) {
return null;
}
//返回连接代理实现,并生成连接ID
return new ConnectionProxyImpl(dataSource, nativeConnection, info, dataSource.createConnectionId());
}
3.生成DruidPooledPreparedStatement对象
//初始化语句,此处将对SQL的超时时间进行设置,如果开启事务,则取transactionQueryTimeout,否则取queryTimeout
initStatement(stmtHolder);
//生成DruidPooledPreparedStatement对象
DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
//添加对预编译对象的跟踪
holder.addTrace(rtnVal);
return rtnVal;