在上一篇文章我們分析到了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);
}