一,前言
我们在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码的最后一步说到执行查询的关键代码:
result = sqlSession.selectOne(command.getName(), param);
selelectOne方法有两个参数:
第一个参数是:com.zcz.learnmybatis.dao.UserDao.findUserById
第二个参数是:1(Integer类型,就是我们传入的参数id:1,我们是期望通过这个id查询到我们想要的结果)
因为接下来的代码比较复杂,而且容易迷路。那么我们就定下来本片文章的目的:
1,sql语句是什么时候,在哪里执行的?
2,我们传入参数id是怎么参与执行的?
为了更情绪的分析这两个问题的答案,我将分析的过程分为三步:
1,在SqlSession中的执行过程
2,在Excutor中的执行过程
3,在Statement中的执行过程
二,在代码的执行过程中分析问题
1,代码在SqlSession中的执行过程
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中,我们已经知道了,使用的sqlSession是DefaultSqlSession,那显而易见,要首先看一下selectOne的源码了:
1 DefaultSqlSession implements SqlSession
2 public <T> T selectOne(String statement, Object parameter) {
3 // Popular vote was to return null on 0 results and throw exception on too many.
4 // 执行查询
5 List<T> list = this.<T>selectList(statement, parameter);
6 if (list.size() == 1) {
7 //如果返回的list的大小是1,则返回第一个元素
8 return list.get(0);
9 } else if (list.size() > 1) {
10 //如果大于1,则抛出异常
11 throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
12 } else {
13 // 如果小于1,则返回null
14 return null;
15 }
16 }
到这里可以明白一件事情,原来selectOne是调用selectList执行查询的,只不过是取了返回值的第一个元素。
我们传入的参数id,就是第5行代码的第二个参数,继续分析第5行的源代码:
DefaultSqlSession implements SqlSession
public <E> List<E> selectList(String statement, Object parameter) {
// 调用了另外一个三个参数的重载方法,
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
继续跟踪:
1 DefaultSqlSession implements SqlSession
2 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
3 try {
4 //从configuration中取出解析userDao-mapping.xml文件是生成的MappedStatement
5 MappedStatement ms = configuration.getMappedStatement(statement);
6 // 这里的executor是CachingExecutor
7 List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
8 return result;
9 } catch (Exception e) {
10 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
11 } finally {
12 ErrorContext.instance().reset();
13 }
14 }
看源代码的第5行,还记得在文章:Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析中,mapper文件中的每一个select,insert,update,delete标签,都会解析成一个MappedStatement类的实例对象吗?而且这个实例对象是存放在configuration中的。然后执行第7行代码,这里的executor是CachingExecutor实例对象。到这里SqlSession中的代码流程就结束了,我们进入Executor中的执行过程。
2,代码在在Excutor中的执行过程
看看源代码:
1 CachingExecutor implements Executor
2 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
3 //取出sql语句
4 BoundSql boundSql = ms.getBoundSql(parameterObject);
5 CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
6 return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
7 }
同样的,查询参数id也是在这个方法的第二个参数,而且在取出sql语句的方法中,对查询参数进行了检查。继续跟踪这个方法中的第6行代码:
1 CachingExecutor implements Executor
2 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
3 throws SQLException {
4 //使用缓存
5 Cache cache = ms.getCache();
6 if (cache != null) {
7 flushCacheIfRequired(ms);
8 if (ms.isUseCache() && resultHandler == null) {
9 ensureNoOutParams(ms, parameterObject, boundSql);
10 @SuppressWarnings("unchecked")
11 List<E> list = (List<E>) tcm.getObject(cache, key);
12 if (list == null) {
13 list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
14 tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
15 }
16 return list;
17 }
18 }
19
20 return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
21 }
继续跟踪第20行代码:
1 BaseExecutor implements Executor
2 @SuppressWarnings("unchecked")
3 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
4 ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
5 if (closed) throw new ExecutorException("Executor was closed.");
6 if (queryStack == 0 && ms.isFlushCacheRequired()) {
7 clearLocalCache();
8 }
9 List<E> list;
10 try {
11 queryStack++;
12 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
13 if (list != null) {
14 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
15 } else {
16 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
17 }
18 } finally {
19 queryStack--;
20 }
21 if (queryStack == 0) {
22 for (DeferredLoad deferredLoad : deferredLoads) {
23 deferredLoad.load();
24 }
25 deferredLoads.clear(); // issue #601
26 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
27 clearLocalCache(); // issue #482
28 }
29 }
30 return list;
31 }
继续跟踪第16行代码:
1 BaseExecutor implements Executor
2 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
3 List<E> list;
4 localCache.putObject(key, EXECUTION_PLACEHOLDER);
5 try {
6 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
7 } finally {
8 localCache.removeObject(key);
9 }
10 localCache.putObject(key, list);
11 if (ms.getStatementType() == StatementType.CALLABLE) {
12 localOutputParameterCache.putObject(key, parameter);
13 }
14 return list;
15 }
继续跟踪第6行代码:
1 SimpleExecutor extends BaseExecutor
2 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
3 Statement stmt = null;
4 try {
5 Configuration configuration = ms.getConfiguration();
6 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
7 //准备预处理语句
8 stmt = prepareStatement(handler, ms.getStatementLog());
9 return handler.<E>query(stmt, resultHandler);
10 } finally {
11 closeStatement(stmt);
12 }
13 }
看到第3行了吗?这里声明了一个Statement,是不是很熟悉?是的,这就是我们在使用原始的JDBC进行查询的时候,用到的,那么我们的问题是不是在这里就有了答案了呢?这里先留一个标记。
代码执行到这里之后呢,Executor部分的流程就结束了,接下来是在Statement中的执行过程。
3,代码在Statement中的执行过程
继续看源码:
1 //第一段源码
2 RoutingStatementHandler implements StatementHandler
3 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
4 return delegate.<E>query(statement, resultHandler);
5 }
6 //第二段源码
7 PreparedStatementHandler extends BaseStatementHandler
8 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
9 PreparedStatement ps = (PreparedStatement) statement;
10 ps.execute();
11 return resultSetHandler.<E> handleResultSets(ps);
12 }
在源代码的第10行,是不是也很熟悉?同样也是使用了JDBC进行的查询。似乎这一段的源代码很简单,但其实不是的resultSetHandler中也是做了一部分工作的,这里就不详细描述了。代码到这里就结束了,似乎我们没有看到文章开头的两个问题的答案。看来应该就是在上面标记的地方了。
4,问题的答案
关键代码:stmt = prepareStatement(handler, ms.getStatementLog());
prepareStatement源码:
1 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
2 Statement stmt;
3 //从事务中获取数据库连接
4 Connection connection = getConnection(statementLog);
5 // 获取Statement
6 stmt = handler.prepare(connection);
7 // 为Statement设置查询参数
8 handler.parameterize(stmt);
9 return stmt;
10 }
1 public Statement prepare(Connection connection) throws SQLException {
2 ErrorContext.instance().sql(boundSql.getSql());
3 Statement statement = null;
4 try {
5 //初始化Statement
6 statement = instantiateStatement(connection);
7 //设置查询超时时间
8 setStatementTimeout(statement);
9 setFetchSize(statement);
10 return statement;
11 } catch (SQLException e) {
12 closeStatement(statement);
13 throw e;
14 } catch (Exception e) {
15 closeStatement(statement);
16 throw new ExecutorException("Error preparing statement. Cause: " + e, e);
17 }
18 }
1 protected Statement instantiateStatement(Connection connection) throws SQLException {
2 String sql = boundSql.getSql();
3 if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
4 String[] keyColumnNames = mappedStatement.getKeyColumns();
5 if (keyColumnNames == null) {
6 return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
7 } else {
8 return connection.prepareStatement(sql, keyColumnNames);
9 }
10 } else if (mappedStatement.getResultSetType() != null) {
11 return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
12 } else {
13 //从数据库连接中获取statement
14 return connection.prepareStatement(sql);
15 }
16 }
看到第14行,哈哈这里就是我们要的答案了。这样一来我们的两个问题,就都有答案了。
因为执行查询的这个过程比较复杂,如果真的要详细的全部解释清楚的话,估计还得10几篇文章要写。鉴于作者能力和时间,就大概的展示一下这个过程吧。
远程不易,转载请声明出处:https://www.cnblogs.com/zhangchengzi/p/9712446.html