在上一篇文章我們分析到了mapper接口方法的實現實際上是交由代理類來實現的,並最終調用Executor來查詢,接下來我們對
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)這個方法進行分析。
@Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
ms.getBoundSql內部調用RawSqlSource的getBoundSql方法,該方法又調用了StaticSqlSource的getBoundSql方法,並在該方法內部初始化了一個BoundSql對象,如下是BoundSql的參數
private String sql; //需要執行的sql語句 private List<ParameterMapping> parameterMappings; //參數與數據庫列的對應關系 private Object parameterObject; //查詢傳遞的參數 private Map<String, Object> additionalParameters; private MetaObject metaParameters;
createCacheKey是調用的BaseExecutor方法根據mappedStatement的id,rowBounds的offset、limit值、要執行的sql語句、傳遞的參數、environment的id來創建cacheKey。
在query的時候查看是否有cache,如果有則使用cache結果,否則使用內部delegate的query方法,這里跳轉到了BaseExecutor的query方法,該方法內部又使用了queryFromDatabase方法。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
方法比較簡單將查詢結果保存在chche中,調用doQuery方法,BaseExecutor的doQuery方法是個抽象方法,因此這里實際使用的是子類SimpleExecutor的doQuery方法
@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
我們先看看newStatementHandler這個方法,這個方法主要做了兩件事情,實例化了一個RoutingStatementHandler對象,將攔截目標是statementHandler的攔截器構成攔截鏈。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
RoutingStatementHandler采用的是裝飾設計模式,內部delegate委托的是PreparedStatementHandler對象,因此它的構造方法內部去創建了一個PreparedStatementHandler對象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); }
構建的過程中也初始化了parameterHandler、resultSetHandler兩個對象,顧名思義一個是用來處理參數的一個是用來處理結果的。
所以在創建StatementHandler的同時其余兩個handler也被創建出來了。
接下來使用prepareStatement來構建參數。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; }
在這個方法內部,我們最關注的是倒數二、三句。
handler.prepare類似mapper交由statementHandler的代理對象來執行,若沒有針對其的攔截方法則還是調用RoutingStatementHandler的prepare方法。
public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } }
這里主要做了幾件事:返回preparedStatement對象、設置查詢超時時間、設置每次批量返回的結果行數。
handler.parameterize(stmt)方法類似也是交由statementHandler的代理對象來執行,最終也使用RoutingStatementHandler的parameterize方法。
public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); }
在這個方法里主要是根據定義的入參的javaType、jdbcType類型來選擇合適的typeHandler來設置參數,因為我們使用的是long類型,因此typeHandler使用的是LongTypeHandler。
這樣我們就把preparedStatement所需的參數全部填充了,最終進入handler.<E>query(stmt, resultHandler)方法。
query方法也會先調用攔截鏈的方法,最后使用RoutingStatementHandler的query方法。
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }