Mybatis SqlSession執(zhí)行過程說明

記錄是一種精神,是加深理解最好的方式之一。

最近看了下Mybatis的源碼,了解了下SqlSession執(zhí)行Sql的過程,在這里把他記下來
曹金桂 cao_jingui@163.com(如有遺漏之處還請(qǐng)指教)
時(shí)間:2016年10月5日14:50

SqlSession的delete/update/insert執(zhí)行過程

調(diào)用過程說明

  1. 用戶代碼獲取到SqlSession對(duì)象后(DefaultSqlSession),調(diào)動(dòng)SqlSession的insert/update/delete
public int update(String statement, Object parameter) {   
      try {      
            dirty = true;
            MappedStatement ms = configuration.getMappedStatement(statement); //獲取MappedStatement對(duì)象,此對(duì)象包含了對(duì)應(yīng)Mapper的所有配置信息
            return executor.update(ms, wrapCollection(parameter));   //調(diào)用Executor對(duì)象的update方法
      } catch (Exception e) {     
            throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);   
      } finally {      
            ErrorContext.instance().reset();   
      }
}

SqlSession中的Executor對(duì)象在Configuration中創(chuàng)建的

public Executor newExecutor(Transaction transaction, ExecutorType executorType) { 
      //確保ExecutorType不為空(defaultExecutorType有可能為空) 
      executorType = executorType == null ? defaultExecutorType : executorType;
      executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
      Executor executor;
      // 根據(jù)ExecutorType類別創(chuàng)建Executor對(duì)象
      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); //調(diào)用插件攔截器
      return executor;
}
  1. 通過Executor執(zhí)行Sql操作(這里以SimpleExecutor為例)
    SqlSession的update/insert/delete操作會(huì)調(diào)用BaseExecutor的update方法
public int update(MappedStatement ms, Object parameter) throws SQLException { 
      ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());    
      if (closed) {
            throw new ExecutorException("Executor was closed.");    
      } 
      clearLocalCache(); //先清局部緩存,再更新,如何更新由子類實(shí)現(xiàn),模板方法模式
      return  doUpdate(ms, parameter); //由子類實(shí)現(xiàn)
}

下面看下子類SimpleExecutor的doUpdate方法

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
      Statement stmt = null;
      try {
            Configuration configuration = ms.getConfiguration();
            //獲得statementHandler        
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            stmt = handler.prepare(getConnection(ms.getStatementLog()));  //獲取Statement對(duì)象
            handler.parameterize(stmt);//設(shè)置參數(shù)        
            return handler.update(stmt); //最終是一個(gè)statement進(jìn)行處理 
      } finally {
            closeStatement(stmt);    
      }
}
  1. 繼續(xù)看StatementHandler接口對(duì)象的創(chuàng)建過程
    StatementHandler對(duì)象是通過Configuration的newStatementHandler方法創(chuàng)建的
//創(chuàng)建Statement對(duì)象(**會(huì)調(diào)用過濾器**)
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);
      // 這里會(huì)調(diào)用Mybatis的所有插件,返回代理對(duì)象(責(zé)任鏈模式)
      statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
      return statementHandler;
}

Configuration中創(chuàng)建的是RoutingStatementHandler對(duì)象,其實(shí)這個(gè)對(duì)象就是StatementHandler的代理對(duì)象(靜態(tài)代理),創(chuàng)建過程如下:

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());
        }
}

以上根據(jù)StatementType創(chuàng)建不同的StatementHandler子類,所有RoutingStatementHandler的操作都會(huì)調(diào)用delegate對(duì)象來調(diào)用(<b>靜態(tài)代理模式</b>)。

  1. 以PreparedStatementHandler為例,繼續(xù)看SimpleExecutor.doUpdate方法調(diào)用的實(shí)現(xiàn)
    先看StatementHandler.prepare()方法;這個(gè)方法會(huì)調(diào)用PreparedStatementHandler的父類BaseStatementHandler的prepare方法,父類方法會(huì)調(diào)用子類instantiateStatement的實(shí)現(xiàn)方法創(chuàng)建Statement對(duì)象,然后對(duì)生成的Statement對(duì)象做必要的設(shè)置
public Statement prepare(Connection connection) throws SQLException {
       ErrorContext.instance().sql(boundSql.getSql());
       Statement statement = null;
       try {
           statement = instantiateStatement(connection);// 子類實(shí)現(xiàn)方法獲取到Statument對(duì)象
           setStatementTimeout(statement); // 配置的設(shè)置
           setFetchSize(statement);
           return statement;
       } catch (Exception e) {
           closeStatement(statement);
           throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
       }
}

繼續(xù)看子類(PreparedStatementHandler)方法instantiateStatement怎么創(chuàng)建Statement對(duì)象

// 看這里的代碼就知道了,就是java JDBC的操作
protected Statement instantiateStatement(Connection connection) throws SQLException {
       String sql = boundSql.getSql(); //獲取執(zhí)行的sql
       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);
           }
       } else if (mappedStatement.getResultSetType() != null) {
           return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
       } else {
           return connection.prepareStatement(sql);
       }
}

以上就是通過PreparedStatementHandler對(duì)象獲取到了JDBC的Statement對(duì)象,那拿到Statement對(duì)象之后,按照J(rèn)DBC的流程肯定就是設(shè)置sql執(zhí)行參數(shù),然后執(zhí)行。 我們回到SimpleExecutor.doUpdate方法,在獲取到Statement對(duì)象之后,調(diào)用了StatementHandler的parameterize來設(shè)置對(duì)應(yīng)的參數(shù)

public void parameterize(Statement statement) throws SQLException {
       //這個(gè)方法就一句代碼,調(diào)用parameterHandler.setParameters方法實(shí)現(xiàn)
       parameterHandler.setParameters((PreparedStatement) statement);
}

我們繼續(xù)看下ParameterHandler的唯一實(shí)現(xiàn)類DefaultParameterHandler,看下具體的setParameters方法實(shí)現(xiàn)

public void setParameters(PreparedStatement ps) throws SQLException {
       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();
                   }
                   //ps.setXXX();設(shè)置參數(shù)值
                   typeHandler.setParameter(ps, i + 1, value, jdbcType);
               }
           }
      }
}

到此我們獲取到了Statement對(duì)象,執(zhí)行的參數(shù)值也設(shè)置好了,最后只要調(diào)用Statement的update方法即可執(zhí)行相應(yīng)的sql語句??碢reparedStatementHandler的update方法實(shí)現(xiàn),很簡(jiǎn)單,返回sql執(zhí)行受影響的行數(shù),如果有自增列則處理

public int update(Statement statement) throws SQLException {
       PreparedStatement ps = (PreparedStatement) statement;
       ps.execute(); //很簡(jiǎn)單,調(diào)用JDBC代碼
       int rows = ps.getUpdateCount(); //獲取sql執(zhí)行受影響的行數(shù)
       Object parameterObject = boundSql.getParameterObject();
       KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
       keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
       return rows;
}
  1. 到此我們SqlSession對(duì)象的insert/update/delete的操作調(diào)用過程結(jié)束。

小結(jié)

在SqlSession接口調(diào)用的insert/update/delete方法中,所有的操作都交給了Executor來操作。SqlSession接口是Mybatis框架暴露的外部接口,而Executor是內(nèi)部的實(shí)現(xiàn)接口。在Executor的實(shí)現(xiàn)中,又是調(diào)用StatementHandler來處理的。當(dāng)然,在調(diào)用StatementHandler設(shè)置參數(shù)時(shí)候,需要ParameterHandler來設(shè)置相應(yīng)的參數(shù),具體如下圖:



當(dāng)然,這里我們分析的是sql的insert/update/delete,沒有分析select。所以沒有涉及到ResultSetHandler接口對(duì)結(jié)果集處理(后面文章繼續(xù))。

Mybatis四大接口對(duì)象(本篇涉及到三個(gè)):Executor StatementHandler ParameterHandler ResultSetHander

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容