今天运行一个执行 sql 的单元测试,看看用 druid 执行一个 sql,在源码中执行了哪些东西?
使用单测试:
package com.alibaba.druid.pool.mysql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Assert;
import com.alibaba.druid.pool.DruidDataSource;
import junit.framework.TestCase;
/**
* Created by wenshao on 16/8/5.
*/
public class MySqlTest extends TestCase {
private DruidDataSource dataSource;
protected void setUp() throws Exception {
dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/kstack?allowMultiQueries=true");
dataSource.setUsername("root");
dataSource.setPassword("aiyinsiben");
dataSource.setFilters("log4j");
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);
}
protected void tearDown() throws Exception {
dataSource.close();
}
public void test_mysql() throws Exception {
Connection connection = dataSource.getConnection();
System.out.println("----------- : " + connection.unwrap(Connection.class).getClass());
Statement stmt = connection.createStatement();
stmt.execute("select * from t;");
ResultSet rs = stmt.getResultSet();
while (rs.next()){
System.out.println(rs.getInt("id"));
}
Assert.assertFalse(rs.isClosed());
// Assert.assertTrue(stmt.getMoreResults());
Assert.assertTrue(rs.isClosed());
ResultSet rs2 = stmt.getResultSet();
Assert.assertFalse(rs2.isClosed());
rs2.close();
Assert.assertTrue(rs2.isClosed());
stmt.close();
connection.close();
}
}
复制代码
源码跟踪
1、Connection connection = dataSource.getConnection(); 获取连接,第一篇文章中大概看了下,直接进入第二部分
2、Statement stmt = connection.createStatement();
@Override
public Statement createStatement() throws SQLException {
checkState();
Statement stmt = null;
try {
stmt = conn.createStatement();
} catch (SQLException ex) {
handleException(ex, null);
}
holder.getDataSource().initStatement(this, stmt);
DruidPooledStatement poolableStatement = new DruidPooledStatement(this, stmt);
holder.addTrace(poolableStatement);
return poolableStatement;
}
复制代码
看下 statement 是干啥的:
The object used for executing a static SQL statement and returning the results it produces.
By default, only one ResultSet object per Statement object can be open at the same time.
Therefore, if the reading of one ResultSet object is interleaved with the
reading of another, each must have been generated by different Statement objects.
All execution methods in the Statement interface implicitly close a current ResultSet
object of the statement if an open one exists.
复制代码
中文释义:用于执行静态 SQL 语句并返回其生成的结果的对象。
3、看下重点 stmt.execute("select * from t;");
@Override
public final boolean execute(String sql) throws SQLException {
checkOpen();
incrementExecuteCount();
transactionRecord(sql);
try {
return stmt.execute(sql);
} catch (Throwable t) {
errorCheck(t);
throw checkException(t, sql);
}
}
复制代码
前面几个都是统计用的,重点是 return stmt.execute(sql)
@Override
public boolean execute(String sql) throws SQLException {
updateCount = null;
lastExecuteSql = sql;
lastExecuteType = StatementExecuteType.Execute;
lastExecuteStartNano = -1L;
lastExecuteTimeNano = -1L;
FilterChainImpl chain = createChain();
firstResultSet = chain.statement_execute(this, sql);
recycleFilterChain(chain);
return firstResultSet;
}
复制代码
跟进到这里:
@Override
public boolean statement_execute(StatementProxy statement, String sql) throws SQLException {
if (this.pos < filterSize) {
return nextFilter().statement_execute(this, statement, sql);
}
return statement.getRawObject().execute(sql);
}
复制代码
跟进到这里:nextFilter().statement_execute(this, statement, sql);
@Override
public boolean statement_execute(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
statementExecuteBefore(statement, sql);
try {
boolean firstResult = super.statement_execute(chain, statement, sql);
statementExecuteAfter(statement, sql, firstResult);
return firstResult;
} catch (SQLException error) {
statement_executeErrorAfter(statement, sql, error);
throw error;
} catch (RuntimeException error) {
statement_executeErrorAfter(statement, sql, error);
throw error;
} catch (Error error) {
statement_executeErrorAfter(statement, sql, error);
throw error;
}
}
复制代码
最终又回到了这里:return statement.getRawObject().execute(sql);
其中 statement.getRawObject()就是获得了敌营的 jdbc driver;
从 statement.getRawObject().getClass() 中可以看出,这个地方已经是 com.mysql.cj.jdbc.StatementImpl, 就是 mysql 的连接器,再往后就是 mysql-connector 的源码。
继续跟进,执行之后,查看结果:
@Override
public final ResultSet getResultSet() throws SQLException {
checkOpen();
try {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return null;
}
DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
addResultSetTrace(poolableResultSet);
return poolableResultSet;
} catch (Throwable t) {
throw checkException(t);
}
}
复制代码
最终又跟进到了 mysql-connector 中:com.mysql.cj.jdbc.StatementImpl#getResultSet
综上所述,druid 连接池,会通过一个 filterchain,把 sql 都路由到 mysql-connector 中调用。下一章我们看下 DataSource 是如何通过参数找到对应的 driver 的。
评论