一,前言html
咱們在文章:Mybatis源碼解析,一步一步從淺入深(二):按步驟解析源碼的最後一步說到執行查詢的關鍵代碼:sql
result = sqlSession.selectOne(command.getName(), param);
selelectOne方法有兩個參數:數據庫
第一個參數是:com.zcz.learnmybatis.dao.UserDao.findUserById緩存
第二個參數是:1(Integer類型,就是咱們傳入的參數id:1,咱們是指望經過這個id查詢到咱們想要的結果)mybatis
由於接下來的代碼比較複雜,並且容易迷路。那麼咱們就定下來本片文章的目的:app
1,sql語句是何時,在哪裏執行的?post
2,咱們傳入參數id是怎麼參與執行的?ui
爲了更情緒的分析這兩個問題的答案,我將分析的過程分爲三步:this
1,在SqlSession中的執行過程spa
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幾篇文章要寫。鑑於做者能力和時間,就大概的展現一下這個過程吧。
遠程不易,轉載請聲明出處:http://www.javashuo.com/article/p-fpdkspdt-dr.html