今天周一,一周的愉快生活就开始了,我们继续来看下Druid的源码。前面我们说到了DruidDataSource
的初始化,以及连接的创建线程和摧毁线程逻辑。我们今天来看下pool包下和查询相关的几个类的逻辑。
第一天,我们来看点比较简单的东西,就是Druid
整个sql
执行流程是什么样的。
SQL执行流程
- 注册驱动
- 初始化数据源
- 从数据源获取连接
- 用连接创建Statment
- 在Statment上创建sql
- 执行sql
- 拿到返回的ResultSet
我们前面主要是对前三点进行了梳理,再来看下后面的几点。
java.sql包知识点复习
看之前先复习一个JDK
下java.sql
包的一些知识,看下包里的几个关键接口/类。
Statement:英文是陈述,我觉得语句更形象,是所有sql语句的一个抽象,当建立连接后,如果你想要操作数据库,需要执行sql,就是将Statement对象将你的sql和数据库传输,并得到执行结果。
Statement会有以下两种子类接口,分别对应两种数据库执行方式:
- PreparedStatement:进行预编译的Statement,如果一个语句会执行多次推荐使用这种方式。
- CallableStatement:执行存储过程的对象。
ResultSet
会拿到返回结果,但是只是一个汇总的信息,实际的信息并没有,需要用rs.next()拿到。但是只能每行每行的拿值。
DruidPooledStatement
然后我们类比着来看看pool包下面的类
我框柱的部分,看着是不是很眼熟,对的,就是和java.sql
里面那些是一样的,只是进行了一些封装而已,加入了“池化”支持。
我们先看下DruidPooledStatement
这个类,不出所料,它实现了Statement
这个接口,所以常规sql操作它都会有,大概说下,前面的DruidDataSource
那个类的init()
方法写的很详细,因为很重要,后面这些不太重要的就不会过得那么细了,只过一下核心部分。这里主要看下它的execute
方法:
@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);
}
}
首先校验连接是否已经被关闭了,在对执行语句计数加一,然后记录执行的sql
。最后执行整个sql
,这里的stmt
是一个接口Statement
,具体实现是在StatementProxyImpl
里的execute
方法。方法如下:
@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;
}
最终实现是在filterChain
里的statement_execute里,这个是先执行filter的逻辑,然后交给具体数据库的jdbc
去实现的。
DruidPooledPreparedStatement
核心代码如下
@Override
public ResultSet executeQuery() throws SQLException {
checkOpen();
incrementExecuteQueryCount();
transactionRecord(sql);
oracleSetRowPrefetch();
conn.beforeExecute();
try {
ResultSet rs = stmt.executeQuery();
if (rs == null) {
return null;
}
DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs);
addResultSetTrace(poolableResultSet);
return poolableResultSet;
} catch (Throwable t) {
errorCheck(t);
throw checkException(t);
} finally {
conn.afterExecute();
}
}
其实和上面代码类似,底层是利用ResultSet rs = stmt.executeQuery()
执行语句和执行结果的。
DruidPooledResultSet
这个类的实现基本就是对ResultSet的一些封装而已,可以直接像ResultSet一样使用。用每列的名字去拿到每行的值。
想法
通过Druid源码可以看出我们可以通过封装一些底层逻辑,在它之上做很多加工,这也是自己定义一个所谓的DruidPooledPreparedStatement
这个类的原因,因为我们可以在封装的类里面做所有想做的逻辑,只要在对应的方法里面在调用以前的实现即可。