前一節咱們已經在看到了sql執行的背後高手Executor(說是高手其實就是SqlSession的高級員工),術業有專攻,當領導任務下達給Executor以後,Executor制定大致的行動方向,接下來就把一切具體的實現交給了本身的小弟StatementHandler,真是名副其實的大懶使小懶呀!java
首先讓咱們來看看StatementHandler中都有誰。sql
接口設計數據庫
public interface StatementHandler { Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; void parameterize(Statement statement) throws SQLException; void batch(Statement statement) throws SQLException; int update(Statement statement) throws SQLException; <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; BoundSql getBoundSql(); ParameterHandler getParameterHandler(); }
根據jdbc中不一樣的statement,核心的三個子類:app
一、SimpleStatementHandler:用於處理Statement數據庫操做ide
二、PreparedStatementHandler:用於處理PreparedStatement數據庫操做函數
三、CallableStatementHandler:用於處理CallableStatement數據庫操做.net
他們三個都繼承自同一個基類BaseStatementHandler,讓咱們來看看這個類設計
BaseStatementHandler 對StatementHandler接口的方法進行了實現,其中只有一個抽象方法交由子類進行實現3d
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
根據方法名,咱們能夠大概知道這個方法是不一樣子類實例化不一樣statement的方法。咱們逐一來看一下。code
/** * SimpleStatementHandler **/ @Override protected Statement instantiateStatement(Connection connection) throws SQLException { //mapper 中未指定結果集類型,直接建立Statement對象 if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.createStatement(); } else {//根據所給定的結果集類型,建立Statement對象 return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } }
**注:**關於結果集類型可參考 jdbc ResultSetType 說明
/** * PreparedStatementHandler **/ @Override protected Statement instantiateStatement(Connection connection) throws SQLException { //獲得執行的Sql String sql = boundSql.getSql(); //判斷是否有主鍵生成器(insert語句須要) if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } //若是沒有,而且ResultSetType是DEFAULT,根據SQL建立 prepareStatement對象 } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.prepareStatement(sql); } else { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } }
/** * CallableStatementHandler **/ @Override protected Statement instantiateStatement(Connection connection) throws SQLException { //獲得執行的Sql String sql = boundSql.getSql(); if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { //若是ResultSetType是DEFAULT,根據SQL建立 prepareStatement對象 return connection.prepareCall(sql); } else { return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } }
根據配置,經過instantiateStatement實例化完成相應的statement以後,接下來讓咱們看看 StatementHandle是如何完成Executor給他的任務的。
不管是query 仍是 update,Executor只讓它執行了兩步任務,準備statement,執行statement。以doQuery爲例咱們來看一看。
//準備statement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
能夠看出,在得到數據庫鏈接以後,Handler經過執行prepare方法,建立好相應的statement。
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); 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); } }
驚不驚喜,意不意外,prepare方法其實就是執行了instantiateStatement方法,並設置了超時時間和每次獲取數據的數量。
接下來就是進行參數的設置。不一樣StatementHandler經過實現parameterize方法實現不一樣的參數設置方式。
/* ** SimpleStatementHandler.parameterize(Statement statement) ** statement直接執行sql,所以不須要參數設置,此處爲空 */ @Override public void parameterize(Statement statement) { // N/A } /* ** PreparedStatementHandler.parameterize(Statement statement) ** 委託parameterHandler進行參數設置 */ @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } /* ** CallableStatementHandler.parameterize(Statement statement) ** 委託parameterHandler進行參數設置 */ @Override public void parameterize(Statement statement) throws SQLException { registerOutputParameters((CallableStatement) statement); parameterHandler.setParameters((CallableStatement) statement); }
ParameterHandler 有惟一的實現類DefaultParameterHandler,讓咱們看看它是如何實現setParameters方法的。
@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)) { 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進行參數設置 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); } } } } }
萬事俱備,只欠東風。一個被安排的明明白白的statement,只須要在適當的時機執行execute() 方法,sql便執行了。至此在經歷了Executor——>StatementHandler這兩個委託者以後,sqlSession終於執行完成了一個SQL語句。PreparedStatementHandler.query()方法爲例:
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; //執行sql ps.execute(); //返回結果集 return resultSetHandler.handleResultSets(ps); }
RoutingStatementHandler 做爲 StatementHandler的實現類,卻在實現是沒有一絲一毫本身的實現邏輯,它更像是一個StatementHandler的策略選擇器,根據Statement type 建立不一樣的StatementHandler。全部方法的實現也都是調用相應StatementHandler的具體實現。讓咱們看看它的構造函數:
private final StatementHandler delegate; 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()); } }
至此,關於StatementHandler的介紹就基本上結束了。咱們已經大致上知道一個sql的執行流程:
SqlSession ——>Exeecutor ——>StatementHandler。
然而TypeHandler是如何set參數的?sql執行成功以後resultHandler是如何映射結果集的?這將是咱們接下來主要研究的內容。