<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      源碼分析——MyBatis核心接口SqlSession的實現(xiàn)原理

      在上一篇文章中(《MyBatis動態(tài)代理調(diào)用過程源碼分析》),我們知道了MyBatis動態(tài)代理的核心是MapperProxy,在它內(nèi)部封裝了動態(tài)代理的調(diào)用邏輯,而我們也知道了在使用動態(tài)代理進行操作的時候?qū)嶋H上還是調(diào)用的SqlSession中的API去實現(xiàn)的,那么我們今天就來分析一波SqlSession的源碼,由于SqlSession中方法很多,我們就已查詢方法為例進行分析。

      一. 核心接口SqlSession

      在這里插入圖片描述

      • SqlSession是MyBatis對外提供的核心接口,通過它可以執(zhí)行數(shù)據(jù)庫讀寫命令、獲取映射器、管理事務(wù)等;
      • DefaultSqlSessionSqlSession接口的實現(xiàn)類,它是真正執(zhí)行數(shù)據(jù)庫操作的門面類,它內(nèi)部封裝著復(fù)雜的數(shù)據(jù)庫操作邏輯;
      • SqlSessionFactorySqlSession的工廠類,負責(zé)創(chuàng)建DefaultSqlSession實例(詳見DefaultSqlSessionFactory::openSession方法);
      • SqlSessionManager是對SqlSession的一種加強,當(dāng)用戶調(diào)用CRUD方法時,會查詢ThreadLocal中 當(dāng)前線程是否已經(jīng)創(chuàng)建SqlSession,如果沒有創(chuàng)建則調(diào)用SqlSessionFactory創(chuàng)建SqlSession調(diào)用 對應(yīng)的方法,如果當(dāng)前線程已經(jīng)創(chuàng)建過SqlSession,則使用緩存的SqlSession

      二. 天下歸一selectList

      SqlSession內(nèi)部有著豐富的查詢接口,他們看似實現(xiàn)著不同的功能,但實際上最終執(zhí)行的都是DefaultSqlSession::selectList(String, Object, RowBounds)方法:

      在這里插入圖片描述

      我們就以selectOne為例子:

      @Override
      public <T> T selectOne(String statement) {
        return this.selectOne(statement, null);
      }
      
      @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many.
        List<T> list = this.selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
      

      可以看到selectOne實際上還是調(diào)用的selectList獲取的結(jié)果集,然后取出結(jié)果集中的第一個返回。

      三. 項目經(jīng)理Executor

      進入DefaultSqlSession::selectList(String, Object, RowBounds)方法我們能夠發(fā)現(xiàn),它調(diào)用的是Executor::query方法:

      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          //獲取Configuration中保存的statementId對應(yīng)的Sql節(jié)點信息
          MappedStatement ms = configuration.getMappedStatement(statement);
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
      

      可以看到SqlSession中所有方法最終都交由了Executor去執(zhí)行了,而Executor就像一個項目經(jīng)理。老板(SqlSession)只需要將任務(wù)交給項目經(jīng)理(Executor),任務(wù)執(zhí)行的具體過程老板并不關(guān)心,它只關(guān)心經(jīng)理對任務(wù)的完成情況。

      Executor繼承體系結(jié)構(gòu):

      在這里插入圖片描述

      3.1 一級緩存管理

      我們先看BaseExecutor分支,它使用了典型的模板方法模式,在BaseExecutor::query中實現(xiàn)了基本的算法骨架,而真正執(zhí)行查詢的方法doQuery交由子類實現(xiàn):

      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        //獲取SQL信息
        BoundSql boundSql = ms.getBoundSql(parameter);
        //根據(jù)statementId、SQL語句、參數(shù)、分頁信息等生產(chǎn)緩存的Key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        //執(zhí)行重載query方法
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
      
      @SuppressWarnings("unchecked")
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        //沒有嵌套查詢且flushCache=tre則清空緩存
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          //查詢層次加一
          queryStack++;
          //查詢一級緩存
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            //緩存命中,處理一級緩存中的輸出參數(shù)(存儲過程)
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            //緩存未命中,從數(shù)據(jù)庫加載數(shù)據(jù),并將結(jié)果放入以及緩存
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            //延遲加載處理
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            //如果當(dāng)前SQL的一級緩存配置為STATEMENT,查詢完既清空緩存
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
      

      可以看到在BaseExecutor::query中實現(xiàn)了一級緩存的基本邏輯。當(dāng)一級緩存沒有命中時會調(diào)用虛方法abstract doQuery,此時這個方法就等著子類去實現(xiàn)了。而在當(dāng)前的MyBatis版本中(3.5.5)BaseExecutor擁有如下子類:

      • BatchExecutor:執(zhí)行批處理操作時使用

      • SimpleExecutor:默認(rèn)配置,使用Statement執(zhí)行SQL語句,每一次執(zhí)行都會創(chuàng)建一個新的Statement。(至于使用PreparedStatement還是Statement執(zhí)行SQL語句,這取決于SQL標(biāo)簽中配置的statementType屬性,默認(rèn)使用PreparedStatement。下同)

      • ReuseExecutor:使用Statement執(zhí)行SQL語句,會重用緩存中的Statement對象。

        • public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            //獲取Statment,與SimpleExecutor中不同的是,ReuseExecutor在這里會先去緩存中尋找Statement,如果沒有再創(chuàng)建
            Statement stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.query(stmt, resultHandler);
          }
          
      • ClosedExecutor:關(guān)閉后的執(zhí)行器,內(nèi)部的所有方法都是未實現(xiàn)的

        • protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            throw new UnsupportedOperationException("Not supported.");
          }
          

      3.2 二級緩存管理

      可以看到在BaseExecutor主要實現(xiàn)了一級緩存的功能,它只有在一級緩存未命中的情況下才會去真正執(zhí)行數(shù)據(jù)庫查詢。但是熟悉MyBatis的小伙伴應(yīng)該知道,MyBatis還提供了二級緩存的實現(xiàn),此時我們就需要將目光轉(zhuǎn)向CachingExecutor分支了。

      CachingExcutor實際上是一個裝飾器,它封裝了二級緩存的實現(xiàn)邏輯

      /**
       * Executor的二級緩存裝飾器,它是實現(xiàn)MyBatis二級緩存的關(guān)鍵
       * @author Clinton Begin
       * @author Eduardo Macarron
       */
      public class CachingExecutor implements Executor {
      
        private final Executor delegate;
        private final TransactionalCacheManager tcm = new TransactionalCacheManager();
      
        public CachingExecutor(Executor delegate) {
          this.delegate = delegate;
          delegate.setExecutorWrapper(this);
        }
      
        //........
                                              
        @Override
        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {
          //獲取二級緩存
          Cache cache = ms.getCache();
          if (cache != null) {
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
              ensureNoOutParams(ms, boundSql);
              //從二級緩存中獲取數(shù)據(jù)
              @SuppressWarnings("unchecked")
              List<E> list = (List<E>) tcm.getObject(cache, key);
              if (list == null) {
                //二級緩存為空,才調(diào)用被裝飾的Executor的query獲取數(shù)據(jù),由于CachingExecutor大多數(shù)時候是用來裝飾SimpleExecutor對象,所以CachingExecutor
                // 中的二級緩存邏輯會先執(zhí)行,如果二級緩存中沒有數(shù)據(jù),才會執(zhí)行SimpleExecutor中一級緩存的邏輯
                list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                tcm.putObject(cache, key, list); // issue #578 and #116
              }
              return list;
            }
          }
          return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
      //......
      
      }
      
      

      可以看到只要被CachingExecutor裝飾過的Executor,都具備了二級緩存的功能。

      走到這里,可能有很多小伙伴對二級緩存的實現(xiàn)原理仍然不太清楚,那這個故事還得從Executor的創(chuàng)建說起,我們先定位到Executor創(chuàng)建的地方DefaultSqlSessionFactory::openSession

      public SqlSession openSession() {
        //從數(shù)據(jù)源中獲取連接,然后創(chuàng)建SqlSessionFactory
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
      

      openSession方法直接調(diào)用了openSessionFromDataSource方法:

      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          //獲取mybatis-config.xml中的enviroment對象
          final Environment environment = configuration.getEnvironment();
          //從Enviroment獲取TranslationFactory
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //從數(shù)據(jù)源中獲取數(shù)據(jù)庫連接,然后創(chuàng)建Transaction對象
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          //重點:根據(jù)配置創(chuàng)建Executor,該方法內(nèi)部會根據(jù)用戶是否配置二級緩存去決定是否創(chuàng)建二級緩存的裝飾器去裝飾Executor,這也是二級緩存是否生效的關(guān)鍵
          final Executor executor = configuration.newExecutor(tx, execType);
          //創(chuàng)建DefaultSqlSession
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
      

      可以看到在openSessionFromDataSource方法中調(diào)用了Configuration::newExecutor方法,它就是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) {
          //創(chuàng)建Batch類型的Executor
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          //創(chuàng)建Reuse類型的Executor
          executor = new ReuseExecutor(this, transaction);
        } else {
          //創(chuàng)建simple類型的Executor
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          //如果配置了二級緩存,則用CachingExecutor裝飾前面創(chuàng)建的Executor,從而實現(xiàn)二級緩存
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
      
      

      當(dāng)用戶配置二級緩存后,就會創(chuàng)建CachingExecutor裝飾器去包裝SimpleExecutor這也是MyBatis二級緩存始終會優(yōu)先于一級緩存的原因

      3.3 項目經(jīng)理不干活

      前面說了這么多,咱還是小小的總結(jié)一下吧。SqlSession的所有查詢方法最后都會交給selectList來執(zhí)行,而selectList將任務(wù)下發(fā)給了項目經(jīng)理(Executor)去執(zhí)行,項目經(jīng)理會先去查二級緩存有沒有已經(jīng)緩存的結(jié)果,如果沒有則會去查一級緩存,如果還是沒有命中則會去執(zhí)行doQuery方法操作數(shù)據(jù)庫。

      好的,那我們把視角轉(zhuǎn)回到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();
          //根據(jù)SQL標(biāo)簽中配置statementType來創(chuàng)建不同的StatementHandler實現(xiàn)
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          //創(chuàng)建Statement,并使用parameterHandler對占位符進行賦值
          stmt = prepareStatement(handler, ms.getStatementLog());
          //通過statementHandler對象調(diào)用ResultSetHandler將結(jié)果集轉(zhuǎn)化為指定對象返回
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
      
      

      doQuery方法實際上也沒有真正執(zhí)行數(shù)據(jù)庫操作,而是將這個艱巨任務(wù)交給了StatementHandler去執(zhí)行了,而這樣看上去確實符合項目經(jīng)理的調(diào)性,自己負責(zé)分配任務(wù),苦逼程序員負責(zé)完成需求,哈哈。

      跳回到計算機的世界,這種設(shè)計思想也完美符合設(shè)計模式中的單一職責(zé)原則,Executor只負責(zé)一二級緩存的實現(xiàn),而數(shù)據(jù)庫的操作交由*Handler實現(xiàn)。

      四. 苦逼程序員

      領(lǐng)導(dǎo)將任務(wù)下發(fā)后,就需要底下的項目組成員去完成了。而在MyBatis中一次操作任務(wù)會下發(fā)給三個“程序員”,它們分別負責(zé)不同的任務(wù):

      • ParameterHandler:對預(yù)編譯的SQL語句進行參數(shù)設(shè)置,DefualtParameterHandler是其實現(xiàn)類
      • StatementHandler: 執(zhí)行SQL語句,獲取JDBC返回的ResultSet
      • ResultSetHandler:對數(shù)據(jù)庫返回的結(jié)果集進行封裝,DefaultResultSetHandler是其實現(xiàn)類

      4.1 ParameterHandler

      在3.3節(jié)中,我們分析了SimpleExecutor:doQuery方法,其內(nèi)部調(diào)用了SimpleExecutor::prepareStatement方法用于獲取PreparedStatement實例并設(shè)置占位符的參數(shù)值:

      private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        //試圖創(chuàng)建帶有日志功能的Connection,也就是ConnectionLogger
        Connection connection = getConnection(statementLog);
        //試圖創(chuàng)建帶有日志功能PreparedStatement,也就是PreparedStatementLogger
        stmt = handler.prepare(connection, transaction.getTimeout());
        //設(shè)置參數(shù)值
        handler.parameterize(stmt);
        return stmt;
      }
      
      

      可以看到SimpleExecutor::prepareStatement方法調(diào)用了Statement:parameterize方法,我們進入PreparedStatementHandler一探究竟:

      public void parameterize(Statement statement) throws SQLException {
        //設(shè)置參數(shù)值
        parameterHandler.setParameters((PreparedStatement) statement);
      }
      
      

      可以看到,設(shè)置參數(shù)值的操作最終是交給了ParameterHandler去實現(xiàn):

      /**
       * 對預(yù)編譯的SQL語句進行參數(shù)設(shè)置
       * @author Clinton Begin
       * @author Eduardo Macarron
       */
      public class DefaultParameterHandler implements ParameterHandler {
      
        //TypeHandler注冊中心
        private final TypeHandlerRegistry typeHandlerRegistry;
      
        //對應(yīng)的SQL節(jié)點信息
        private final MappedStatement mappedStatement;
        //用戶傳入的參數(shù)
        private final Object parameterObject;
        //SQL語句信息
        private final BoundSql boundSql;
        private final Configuration configuration;
      
       //...
      
        @Override
        public void setParameters(PreparedStatement ps) {
          ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
          //從boundSql中獲取sql語句的占位符對應(yīng)的參數(shù)信息
          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) {//當(dāng)參數(shù)為存儲過程輸出參數(shù)則不處理
                //綁定的實參
                Object value;
                //參數(shù)的名稱
                String propertyName = parameterMapping.getProperty();
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                  //如果SQL中的參數(shù)列表中包含這個參數(shù),則獲取值
                  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 typeHandler = parameterMapping.getTypeHandler();
                //獲取參數(shù)對應(yīng)的jdbcType
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                  jdbcType = configuration.getJdbcTypeForNull();
                }
                try {
                  //由typeHandler設(shè)置參數(shù)值
                  typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException | SQLException e) {
                  throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
              }
            }
          }
        }
      
      }
      
      

      4.2 StatementHandler

      MyBatis進行數(shù)據(jù)庫操作的核心是由StatementHandler完成的,而StatementHandler只是一個接口,它擁有如下幾個實現(xiàn)類:

      • BaseStatementHandler:所有子類的父類,模板方法模式,內(nèi)部定義了數(shù)據(jù)庫操作的操作步驟,核心功能交由子類實現(xiàn)。
      • SimpleStatmentHandler :使用Statement對象訪問數(shù)據(jù)庫,無須參數(shù)化;
      • PreparedStatmentHandler:使用預(yù)編譯PrepareStatement對象訪問數(shù)據(jù)庫;
      • CallableStatmentHandler :調(diào)用存儲過程;

      在這里插入圖片描述

      我們再次回到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();
          //根據(jù)SQL標(biāo)簽中配置statementType來創(chuàng)建不同的StatementHandler實現(xiàn)
          StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
          //創(chuàng)建Statement,并使用parameterHandler對占位符進行賦值
          stmt = prepareStatement(handler, ms.getStatementLog());
          //通過statementHandler對象調(diào)用ResultSetHandler將結(jié)果集轉(zhuǎn)化為指定對象返回
          return handler.query(stmt, resultHandler);
        } finally {
          closeStatement(stmt);
        }
      }
      
      

      doQuery最后調(diào)用的是StamentHandler::query方法,我們進入其實現(xiàn)類中(PreparedStatementHandler::query)可以看到最終還是調(diào)用的JDBC中PreparedStatement::execute方法:

      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        //執(zhí)行SQL語句
        ps.execute();
        //封裝結(jié)果集并返回對象
        return resultSetHandler.handleResultSets(ps);
      }
      
      

      4.3 ResultSetHandler

      ResultSetHandler的唯一實現(xiàn)類DefaultResultSetHandler用于對返回結(jié)果集進行包裝。

      DefaultResultSetHandler::handleResultSets源碼:

       public List<Object> handleResultSets(Statement stmt) throws SQLException {
          ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
      
          //用于保存結(jié)果集對象
          final List<Object> multipleResults = new ArrayList<>();
      
          int resultSetCount = 0;
          //statement可能返回多個結(jié)果集對象,這里先取出第一個結(jié)果集
          ResultSetWrapper rsw = getFirstResultSet(stmt);
      
          //獲取映射規(guī)則,對應(yīng)映射配置文件中resultMap標(biāo)簽。有時候sql標(biāo)簽的resultMap屬性會指定多個結(jié)果集。
          List<ResultMap> resultMaps = mappedStatement.getResultMaps();
          int resultMapCount = resultMaps.size();
          //結(jié)果集和resultMap不能為空,為空拋出異常
          validateResultMapsCount(rsw, resultMapCount);
          while (rsw != null && resultMapCount > resultSetCount) {
            //循環(huán)處理多個結(jié)果集
            //獲取當(dāng)前結(jié)果集對應(yīng)的resultMap
            ResultMap resultMap = resultMaps.get(resultSetCount);
            //根據(jù)映射規(guī)則對結(jié)果進行轉(zhuǎn)化,轉(zhuǎn)換成目標(biāo)對象以后放入multipleResults
            handleResultSet(rsw, resultMap, multipleResults, null);
            //獲取下一個結(jié)果集
            rsw = getNextResultSet(stmt);
            //清空nestedResultObjects對象
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
          }
      
          String[] resultSets = mappedStatement.getResultSets();
          if (resultSets != null) {
            while (rsw != null && resultSetCount < resultSets.length) {
              ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
              if (parentMapping != null) {
                String nestedResultMapId = parentMapping.getNestedResultMapId();
                ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                handleResultSet(rsw, resultMap, null, parentMapping);
              }
              rsw = getNextResultSet(stmt);
              cleanUpAfterHandlingResultSet();
              resultSetCount++;
            }
          }
      
          return collapseSingleResultList(multipleResults);
        }
      
      

      封裝單個結(jié)果集:

       /**
         * 封裝單個結(jié)果集數(shù)據(jù),將結(jié)果保存至multipleResults中
         * @param rsw
         * @param resultMap
         * @param multipleResults
         * @param parentMapping
         * @throws SQLException
         */
        private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
          try {
            if (parentMapping != null) {
              //處理嵌套映射
              handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
              if (resultHandler == null) {
                //如果resultHandler為空則,實例化一個默認(rèn)的resultHandler
                DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                //封裝結(jié)果集數(shù)據(jù),然后將數(shù)據(jù)暫存在defaultResultHandler中
                handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                //將暫存在defaultResultHandler中的數(shù)據(jù)放入multipleResults中
                multipleResults.add(defaultResultHandler.getResultList());
              } else {
                handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
              }
            }
          } finally {
            // issue #228 (close resultsets)
            closeResultSet(rsw.getResultSet());
          }
        }
      
      
      public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
          if (resultMap.hasNestedResultMaps()) {
            //處理嵌套結(jié)果集的情況
            ensureNoRowBounds();
            checkResultHandler();
            handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
          } else {
            //處理沒有嵌套結(jié)果集的情況
            handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
          }
        }
      
      
      /**
         * 根據(jù)一個簡單ResultMap規(guī)則,封裝結(jié)果集數(shù)據(jù),然后將數(shù)據(jù)暫存至resultHandler
         * @param rsw
         * @param resultMap
         * @param resultHandler 封裝結(jié)果的暫存區(qū)
         * @param rowBounds
         * @param parentMapping
         * @throws SQLException
         */
        private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
            throws SQLException {
          //創(chuàng)建結(jié)果上下文,所謂的上下文就是專門在循環(huán)中緩存結(jié)果對象的
          DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
          ResultSet resultSet = rsw.getResultSet();
          //根據(jù)分頁信息,定位到指定記錄
          skipRows(resultSet, rowBounds);
          //循環(huán)處理每一行數(shù)據(jù)
          while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
            //進一步完善resultMap信息,主要處理鑒別器信息
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
            //重點解讀:讀取ResultSet中的一行數(shù)據(jù)進行映射,轉(zhuǎn)化并返回目標(biāo)對象
            Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
            //保存映射結(jié)果對象
            storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
          }
        }
      
      

      封裝單行數(shù)據(jù):

      /**
         * 讀取一行數(shù)據(jù),并封裝成指定類型對象中,實例封裝的核心
         * @param rsw
         * @param resultMap
         * @param columnPrefix
         * @return
         * @throws SQLException
         */
        private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
          final ResultLoaderMap lazyLoader = new ResultLoaderMap();
          //創(chuàng)建封裝對象,空的
          Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
          if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            //結(jié)果集不為空,且存在結(jié)果對象的類型處理程序
            //封裝MetaObject對象,方便賦值
            final MetaObject metaObject = configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            if (shouldApplyAutomaticMappings(resultMap, false)) {
              //進行自動映射。一般情況下autoMappingBehavior的默認(rèn)值為PARTIAL,對未明確指定映射規(guī)則的字段進行自動映射
              //是否自動映射由resultMap標(biāo)簽中的autoMapping屬性決定,默認(rèn)值為true
              foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }
            //進行屬性映射
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            //如果發(fā)現(xiàn)數(shù)據(jù)或者懶加載機制中還有沒有加載出來的數(shù)據(jù)
            foundValues = lazyLoader.size() > 0 || foundValues;
            //如果沒有數(shù)據(jù)且不允許空行返回實例,則將rowValue置為空
            rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
          }
          return rowValue;
        }
      
      
      

      五. 時序圖

      在這里插入圖片描述

      由于圖片版幅較大,網(wǎng)頁顯示字體會看不清,這里給出下載鏈接:MyBatis核心調(diào)用流程時序圖-GitHubMyBatis核心調(diào)用流程時序圖-Gitee

      六. 總結(jié)

      SqlSession作為MyBatis框架的核心接口隱藏了底層Executor接口以及xxxHandler接口復(fù)雜的實現(xiàn)邏輯。用戶對SqlSession的操作實際上都交由Executor去執(zhí)行,在Executor處理完一二級緩存邏輯后,就將數(shù)據(jù)庫操作以及結(jié)果集封裝的工作全部交由ParameterHandlerStatementHandlerResultSetHandler去完成。

      本文只是展現(xiàn)了SqlSession查詢數(shù)據(jù)庫框架的基本實現(xiàn)流程,更加細節(jié)的信息需要小伙伴們自己去挖掘。博主自己對MyBatis源碼進行了詳細注釋,如有需要,請移步至:GitHubGitee

      本文闡述了自己對MyBatis源碼的一些理解,如有不足,歡迎大佬指點,感謝感謝!!

      posted @ 2020-05-04 13:00  聽到微笑  閱讀(14)  評論(0)    收藏  舉報  來源
      主站蜘蛛池模板: 欧美极品色午夜在线视频| 成人毛片一区二区| 18禁无遮挡啪啪无码网站| 一本色道久久综合无码人妻| 国产成人精品一区二区三区| 福利成人午夜国产一区| 亚洲av日韩av永久无码电影| 国产不卡一区不卡二区| 国产又色又刺激高潮视频| 欧洲国产成人久久精品综合| 乱码午夜-极品国产内射| 日韩av一区二区三区在线| 毛葺葺老太做受视频| 久久国产精品乱子乱精品| 国产精品天干天干综合网| 116美女极品a级毛片| 久久天天躁狠狠躁夜夜2020老熟妇| 亚洲av在线观看| 精品人妻中文无码av在线| 日本强好片久久久久久aaa| 日韩精品自拍偷拍一区二区| 国产精品视频亚洲二区| 成人3D动漫一区二区三区| 人人妻人人澡人人爽人人精品av | 人妻中出无码中字在线| 国产精品老熟女乱一区二区| 久久91精品牛牛| 国产乱码精品一区二区上| 亚洲欧美国产日韩天堂区| 日韩av综合中文字幕| 蜜桃亚洲一区二区三区四| 99久久精品费精品国产一区二| 亚洲色成人一区二区三区| 亚洲精品色在线网站| 国产偷国产偷亚洲综合av| 国产伦码精品一区二区| 亚洲男女羞羞无遮挡久久丫| 亚洲国产女性内射第一区| 国产一区二区三区精品综合 | 人妻中文字幕亚洲精品| 无遮高潮国产免费观看|