前言:
之前的SqlSession的運作過程還未講完,還差一個SqlSession下的最重要的四大對象,這一篇文章将會講解四大對象之間的關系.
SqlSession下的四大對象
之前講解了映射器其實就是一個動态代理的對象,進入到MapperMethod的execute方法,那麼進入到execute的方法又是如何執行的呢?肯定是通過我們所寫的Sql語句,但是我們關心的不是這些細節,而是架構的設計這些,現在我們來分析這些對象.
1.Executor 執行器,用于排程其他對象
2.StatementHandler 調用statement執行操作
3.ParameterHandler 處理Sql參數
4.ResultHandler 處理最後的結果集封裝并傳回
一.Executor
用于與資料庫進行互動,為我們提供了查詢,更新,事務等方法,這和其他架構并無不同,那麼先讓我們看看Executor如何建立的把
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
他會根據你選擇的三種執行器的哪一種去建立一個對象,建立對象後回去執行最後這句(Executor) interceptorChain.pluginAll(executor),其實這句就是mybatis的插件,它為我們建構了動态代理對象,然後在Executor執行之前先配置mybatis插件.
然後我們再從他的一個BaseExecutor中的updata再來進行分析
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
上面内容很顯然Mybatis根據Configuration來建構StatementHandler,然後使用prepareStatement對SQL語句進行編譯,它先調用了StatementHandler的prepare()方法進行預編譯,然後通過StatementHandler的parameterize()方法來設定參數,resultHandler組裝結果并傳回結果,看到上面這段代碼,我們的注意力就要從Executor中出來了,那麼再讓我們去分析一下StatementHandler這個對象吧.
二.StatementHandler
StatementHandler專門用來處理資料庫之間的會話類,同樣的我們來先看看Mybatis如何建立這個類的把.
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隻是實作了StatementHandler這個接口,那麼它和Executor一樣,都隻是用代理對象進行了一步步的封裝,他和Executor一樣為我們提供了三種執行器分别時SimpleStatementHandler,PreparedStatementHandler,
CallableStatementHandler,他會根據上下文決定建立那個執行器
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
這是RouingStatementHandler類下的構造方法,它提供了一個是配置delegate,他的作用是給實作類對象的使用提供一個統一,簡易的擴充卡.再來看看他的主要三個方法之一的prepare,之後再按照順序執行parameterize,query
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);
}
}
以上代碼中的instantiateStatement方法是對SQL進行了預編譯和基礎配置然後調用parameterize方法去設定參數
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
繼續檢視query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
現在就非常明确了,在得到DefaultSqlSession對象時初始化了Executor執行器。然後在調用Executor的doQuery方法查詢的時候,會初始化StatementHandler對象,然後在初始化StatementHandler的過程中,會初始化ParameterHandler和ResultSetHandler對象,生成的StatementHandler對象會儲存這兩個對象的引用。直到這時,Executor的職責就差不多完成了,通過其他的三大元件來完成接下來的資料庫查詢工作并傳回結果。
三.ParameterHandler
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
getParameterObject方法作用是傳回參數對象,setParameter設定預編譯SQL語句的參數.
那麼我們來分析一下setParameter的實作吧.
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
}
從parameterObject中取參數,根據typeHandler進行參數處理.typeHandler在Mybatis初始化的時候,就已經注冊在了Configuration中.
四.ResultHandler
接口代碼我就不貼出來了,根據前面的描述它隻是對最終的結果進行了封裝處理,我就不在這裡多廢話了.
總結:
SqlSession是通過Executor建立StatementHandler來運作,二StatementHandler要進過下面三步:
1.prepared 預編譯
2.parameterize 設定參數
3. query 執行SQL.
以上就是SqlSession的執行過程,明白了四大對象是如何協作的了.