1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Mybatis SqlSession執(zhí)行流程

        共 15638字,需瀏覽 32分鐘

         ·

        2021-07-11 06:02

        點(diǎn)擊上方「Java有貨」關(guān)注我們


        技術(shù)交流群添加方式


        +



        添加小編微信:372787553,備注:進(jìn)群
        帶您進(jìn)入Java技術(shù)交流群

        Mybatis執(zhí)行SQL流程

        在看源碼之前,我們需要了解一些基本知識(shí),如果您沒(méi)有閱讀Mybatis SqlSessionFactory 初始化原理,可以先閱讀Mybatis SqlSessionFactory 初始化原理這篇文章,這用更有助于我們理解接下來(lái)的文章

        SqlSession


        SqlSession是一個(gè)接口,它有兩個(gè)實(shí)現(xiàn)類(lèi):
        - DefaultSqlSession:默認(rèn)實(shí)現(xiàn)類(lèi)
        - SqlSessionManager:已經(jīng)棄用的實(shí)現(xiàn)類(lèi),所以我們不需要關(guān)注他
        SqlSession是與數(shù)據(jù)庫(kù)交互的頂層類(lèi),通常與ThreadLocal綁定,一個(gè)會(huì)話(huà)使用一個(gè)SqlSession,SqlSession是線程不安全的,使用完畢需要close()

        public class DefaultSqlSession implements SqlSession {
        private final Configuration configuration;
        private final Executor executor;
        }
        SqlSession中最重要的兩個(gè)變量:
        - Configuration:核心配置類(lèi),也是初始化時(shí)傳過(guò)來(lái)的
        - Executor:實(shí)際執(zhí)行SQL的執(zhí)行器

        Executor


        Executor是一個(gè)接口,有三個(gè)實(shí)現(xiàn)類(lèi)
        - BatchExecutor 重用語(yǔ)句,并執(zhí)行批量更新
        - ReuseExecutor 重用預(yù)處理語(yǔ)句prepared statements
        - SimpleExecutor 普通的執(zhí)行器,默認(rèn)使用

        了解完基本知識(shí)后,我們接著往下看代碼。

        當(dāng)創(chuàng)建完SqlSessionFactory后,就可以創(chuàng)建SqlSession,然后使用SqlSession進(jìn)行增刪改查:

        // 1. 讀取配置文件,讀成字節(jié)輸入流,注意:現(xiàn)在還沒(méi)解析InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");// 2. 解析配置文件,封裝Configuration對(duì)象   創(chuàng)建DefaultSqlSessionFactory對(duì)象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();List<Object> objects = sqlSession.selectList("namespace.id");

        我們先去看openSession()方法,創(chuàng)建了SqlSession

        //6. 進(jìn)入openSession方法@Overridepublic SqlSession openSession() {  //getDefaultExecutorType()傳遞的是SimpleExecutor  // level:數(shù)據(jù)庫(kù)事物級(jí)別,null  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}
        //7. 進(jìn)入openSessionFromDataSource。//ExecutorType 為Executor的類(lèi)型,TransactionIsolationLevel為事務(wù)隔離級(jí)別,autoCommit是否開(kāi)啟事務(wù)//openSession的多個(gè)重載方法可以指定獲得的SeqSession的Executor類(lèi)型和事務(wù)的處理private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 獲得 Environment 對(duì)象 final Environment environment = configuration.getEnvironment(); // 創(chuàng)建 Transaction 對(duì)象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 創(chuàng)建 Executor 對(duì)象 final Executor executor = configuration.newExecutor(tx, execType); // 創(chuàng)建 DefaultSqlSession 對(duì)象 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { // 如果發(fā)生異常,則關(guān)閉 Transaction 對(duì)象 closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }}

        通過(guò)源碼可以清晰的看到,會(huì)話(huà)工廠創(chuàng)建了Environment,Transaction,Executor,DefaultSqlSession對(duì)象,并且對(duì)于會(huì)話(huà)對(duì)象來(lái)說(shuō),他的autoCommit默認(rèn)為false,默認(rèn)不自動(dòng)提交。

        然后我回到原來(lái)的代碼,接著就需要使用SqlSession進(jìn)行增刪改查操作了

        所以我們進(jìn)入selectList()查看

        //8.進(jìn)入selectList方法,多個(gè)重載方法@Overridepublic <E> List<E> selectList(String statement) {  return this.selectList(statement, null);}
        @Overridepublic <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT);}
        @Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 獲得 MappedStatement 對(duì)象 MappedStatement ms = configuration.getMappedStatement(statement); // 執(zhí)行查詢(xún) 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(); }}

        selectList有多個(gè)重載方法,進(jìn)入到最終方法后,我們可以看到它做了兩件事

        • 通過(guò)statementId,從Configuration中取MappedStatement對(duì)象,就是存放了sql語(yǔ)句,返回值類(lèi)型,輸入值類(lèi)型的對(duì)象

        • 然后委派Executor執(zhí)行器去執(zhí)行具體的增刪改查方法

        所以,對(duì)于實(shí)際JDBC的操作,我們還需要進(jìn)入Executor中查看

        Executor

        我們繼續(xù)從剛剛selectList源碼中,進(jìn)入Executor查看

        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        //此方法在SimpleExecutor的父類(lèi)BaseExecutor中實(shí)現(xiàn)@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //根據(jù)傳入的參數(shù)動(dòng)態(tài)獲得SQL語(yǔ)句,最后返回用BoundSql對(duì)象表示 BoundSql boundSql = ms.getBoundSql(parameter); //為本次查詢(xún)創(chuàng)建緩存的Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // 查詢(xún) return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

        拆分成了三大步:

        (1)先調(diào)用MappedStatementgetBoundSql方法,獲取解析后的SQL語(yǔ)句,解析工作是由SqlSourceBuilder完成的

        什么叫解析后的SQL語(yǔ)句呢?因?yàn)镸ybatis編寫(xiě)SQL語(yǔ)句時(shí),會(huì)用到動(dòng)態(tài)SQL,比如#{}占位符,這種占位符JDBC是不認(rèn)識(shí)的,所以需要將其轉(zhuǎn)換成占位符,并且將其內(nèi)部的字段名存儲(chǔ)起來(lái),后面填充參數(shù)的時(shí)候好使用反射獲取值。

        /*** 執(zhí)行解析原始 SQL ,成為 SqlSource 對(duì)象** @param originalSql 原始 SQL* @param parameterType 參數(shù)類(lèi)型* @param additionalParameters 附加參數(shù)集合??赡苁强占希部赡苁?{@link org.apache.ibatis.scripting.xmltags.DynamicContext#bindings} 集合* @return SqlSource 對(duì)象*/public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {  // 創(chuàng)建 ParameterMappingTokenHandler 對(duì)象  ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);  // 創(chuàng)建 GenericTokenParser 對(duì)象  GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);  // 執(zhí)行解析  String sql = parser.parse(originalSql);  // 創(chuàng)建 StaticSqlSource 對(duì)象  return new StaticSqlSource(configuration, sql, handler.getParameterMappings());}

        上面代碼就可以看到,會(huì)將拆分#{},進(jìn)行解析

        (2)根據(jù)查詢(xún)條件,創(chuàng)建緩存key,用來(lái)接下來(lái)去緩存查找是否有已經(jīng)執(zhí)行過(guò)的結(jié)果

        (3)調(diào)用重載query()方法

        接著我們進(jìn)入重載方法查看:

        @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());      // 已經(jīng)關(guān)閉,則拋出 ExecutorException 異常      if (closed) {          throw new ExecutorException("Executor was closed.");      }      // 清空本地緩存,如果 queryStack 為零,并且要求清空本地緩存。      if (queryStack == 0 && ms.isFlushCacheRequired()) {          clearLocalCache();      }      List<E> list;      try {          // queryStack + 1          queryStack++;          // 從一級(jí)緩存中,獲取查詢(xún)結(jié)果          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;          // 獲取到,則進(jìn)行處理          if (list != null) {              handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);          // 獲得不到,則從數(shù)據(jù)庫(kù)中查詢(xún)          } else {              list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);          }      } finally {          // queryStack - 1          queryStack--;      }      if (queryStack == 0) {          // 執(zhí)行延遲加載          for (DeferredLoad deferredLoad : deferredLoads) {              deferredLoad.load();          }          // issue #601          // 清空 deferredLoads          deferredLoads.clear();          // 如果緩存級(jí)別是 LocalCacheScope.STATEMENT ,則進(jìn)行清理          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {              // issue #482              clearLocalCache();          }      }      return list;  }

        主要的邏輯:

        • 從一級(jí)緩存取數(shù)據(jù),如果有直接使用緩存的進(jìn)行接下來(lái)的操作

        • 如果沒(méi)有,從數(shù)據(jù)庫(kù)查詢(xún)

        進(jìn)入queryFromDatabase()方法:

        // 從數(shù)據(jù)庫(kù)中讀取操作private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {  List<E> list;  // 在緩存中,添加占位對(duì)象。此處的占位符,和延遲加載有關(guān),可見(jiàn) `DeferredLoad#canLoad()` 方法  localCache.putObject(key, EXECUTION_PLACEHOLDER);  try {      // 執(zhí)行讀操作      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);  } finally {      // 從緩存中,移除占位對(duì)象      localCache.removeObject(key);  }  // 添加到緩存中  localCache.putObject(key, list);  // 暫時(shí)忽略,存儲(chǔ)過(guò)程相關(guān)  if (ms.getStatementType() == StatementType.CALLABLE) {      localOutputParameterCache.putObject(key, parameter);  }  return list;}
        @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); // 傳入?yún)?shù)創(chuàng)建StatementHanlder對(duì)象來(lái)執(zhí)行查詢(xún) StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 創(chuàng)建jdbc中的statement對(duì)象 stmt = prepareStatement(handler, ms.getStatementLog()); // 執(zhí)行 StatementHandler ,進(jìn)行讀操作 return handler.query(stmt, resultHandler); } finally { // 關(guān)閉 StatementHandler 對(duì)象 closeStatement(stmt); }}

        通過(guò)代碼可以看到,對(duì)于實(shí)際與JDBC交互的代碼,Executor也懶得搞,又像SqlSession一樣,委派給小弟StatementHandler了。

        StatementHandler


        我們從剛剛的Executor的代碼查看

        @Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {  Statement stmt = null;  try {      Configuration configuration = ms.getConfiguration();      // 傳入?yún)?shù)創(chuàng)建StatementHanlder對(duì)象來(lái)執(zhí)行查詢(xún)      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);      // 創(chuàng)建jdbc中的statement對(duì)象      stmt = prepareStatement(handler, ms.getStatementLog());      // 執(zhí)行 StatementHandler ,進(jìn)行讀操作      return handler.query(stmt, resultHandler);  } finally {      // 關(guān)閉 StatementHandler 對(duì)象      closeStatement(stmt);  }}


        可以看到,這里創(chuàng)建完StatementHandler后,回調(diào)用prepareStatement()方法,用來(lái)創(chuàng)建Statement對(duì)象

        我們進(jìn)入prepareStatement方法中查看

        // 初始化 StatementHandler 對(duì)象private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {  Statement stmt;  // 獲得 Connection 對(duì)象  Connection connection = getConnection(statementLog);  // 創(chuàng)建 Statement 或 PrepareStatement 對(duì)象  stmt = handler.prepare(connection, transaction.getTimeout());  // 設(shè)置 SQL 上的參數(shù),例如 PrepareStatement 對(duì)象上的占位符  handler.parameterize(stmt);  return stmt;}
        @Overridepublic void parameterize(Statement statement) throws SQLException { //使用ParameterHandler對(duì)象來(lái)完成對(duì)Statement的設(shè)值 parameterHandler.setParameters((PreparedStatement) statement);}

        這里可以看到,它實(shí)際是使用ParameterHandler來(lái)設(shè)置Statement的參數(shù)

        @Overridepublic void setParameters(PreparedStatement ps) {  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());  // 遍歷 ParameterMapping 數(shù)組  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  if (parameterMappings != null) {      for (int i = 0; i < parameterMappings.size(); i++) {          // 獲得 ParameterMapping 對(duì)象          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、jdbcType 屬性              TypeHandler typeHandler = parameterMapping.getTypeHandler();              JdbcType jdbcType = parameterMapping.getJdbcType();              if (value == null && jdbcType == null) {                  jdbcType = configuration.getJdbcTypeForNull();              }              // 設(shè)置 ? 占位符的參數(shù)              try {                  typeHandler.setParameter(ps, i + 1, value, jdbcType);              } catch (TypeException | SQLException e) {                  throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);              }          }      }  }}

        這段代碼的主要目的,就是獲取入?yún)?,然后根?jù)值,來(lái)設(shè)置?占位符的參數(shù)

        TypeHandler是具體進(jìn)行參數(shù)設(shè)置的對(duì)象

        所以handler.prepare(connection, transaction.getTimeout());方法,就是使用ParameterHandler來(lái)對(duì)占位符位置的參數(shù)進(jìn)行值設(shè)置

        然后我們回到Executor,查看handler.query()方法

        @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {  PreparedStatement ps = (PreparedStatement) statement;  // 執(zhí)行查詢(xún)  ps.execute();  // 處理返回結(jié)果  return resultSetHandler.handleResultSets(ps);}

        代碼很簡(jiǎn)單,這里直接使用JDBC的PreparedStatement來(lái)進(jìn)行SQL執(zhí)行,然后使用ResultSetHandler進(jìn)行結(jié)果數(shù)據(jù)封裝處理。

        進(jìn)入ResultSetHandler

        @Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
        // 多 ResultSet 的結(jié)果集合,每個(gè) ResultSet 對(duì)應(yīng)一個(gè) Object 對(duì)象。而實(shí)際上,每個(gè) Object 是 List<Object> 對(duì)象。 // 在不考慮存儲(chǔ)過(guò)程的多 ResultSet 的情況,普通的查詢(xún),實(shí)際就一個(gè) ResultSet ,也就是說(shuō),multipleResults 最多就一個(gè)元素。 final List<Object> multipleResults = new ArrayList<>();
        int resultSetCount = 0; // 獲得首個(gè) ResultSet 對(duì)象,并封裝成 ResultSetWrapper 對(duì)象 ResultSetWrapper rsw = getFirstResultSet(stmt);
        // 獲得 ResultMap 數(shù)組 // 在不考慮存儲(chǔ)過(guò)程的多 ResultSet 的情況,普通的查詢(xún),實(shí)際就一個(gè) ResultSet ,也就是說(shuō),resultMaps 就一個(gè)元素。 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); // 校驗(yàn) while (rsw != null && resultMapCount > resultSetCount) { // 獲得 ResultMap 對(duì)象 ResultMap resultMap = resultMaps.get(resultSetCount); // 處理 ResultSet ,將結(jié)果添加到 multipleResults 中 handleResultSet(rsw, resultMap, multipleResults, null); // 獲得下一個(gè) ResultSet 對(duì)象,并封裝成 ResultSetWrapper 對(duì)象 rsw = getNextResultSet(stmt); // 清理 cleanUpAfterHandlingResultSet(); // resultSetCount ++ resultSetCount++; }
        // 因?yàn)?`mappedStatement.resultSets` 只在存儲(chǔ)過(guò)程中使用,本系列暫時(shí)不考慮,忽略即可 // ···
        // 如果是 multipleResults 單元素,則取首元素返回 return collapseSingleResultList(multipleResults);}
        // 處理 ResultSet ,將結(jié)果添加到 multipleResults 中private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try { // 暫時(shí)忽略,因?yàn)橹挥写鎯?chǔ)過(guò)程的情況,調(diào)用該方法,parentMapping 為非空 if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping); } else { // 如果沒(méi)有自定義的 resultHandler ,則創(chuàng)建默認(rèn)的 DefaultResultHandler 對(duì)象 if (resultHandler == null) { // 創(chuàng)建 DefaultResultHandler 對(duì)象 DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory); // 處理 ResultSet 返回的每一行 Row handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null); // 添加 defaultResultHandler 的處理的結(jié)果,到 multipleResults 中 multipleResults.add(defaultResultHandler.getResultList()); } else { // 處理 ResultSet 返回的每一行 Row handleRowValues(rsw, resultMap, resultHandler, rowBounds, null); } } } finally { // issue #228 (close resultsets) // 關(guān)閉 ResultSet 對(duì)象 closeResultSet(rsw.getResultSet()); }}

        代碼比較多,實(shí)際最重要的代碼就是

        // 添加 defaultResultHandler 的處理的結(jié)果,到 multipleResults 中multipleResults.add(defaultResultHandler.getResultList());

        將處理后的結(jié)果封裝到集合中返回,這樣基本Mybatis邏輯就走完了.

        我們來(lái)回顧一下,都用到了哪些類(lèi)

        簡(jiǎn)單總結(jié)


        SqlSessionFactoryBuilder:

        • 解析核心配置文件,創(chuàng)建Configuration

          • XMLConfigBuilder.parse():解析核心配置文件

          • XMLMapperBuilder.parse():解析映射配置文件MappedStatement

        • 創(chuàng)建SqlSessionFactory,默認(rèn)創(chuàng)建DefaultSqlSessionFactory

        SqlSessionFactory:

        • openSession():構(gòu)建Executor,SqlSession等

        SqlSession:

        • 根據(jù)statementId獲取MappedStatement

        • 委派給Executor執(zhí)行器執(zhí)行

        Executor:

        • 使用SqlSourceBuilder,將SQL解析成JDBC認(rèn)可的

        • 查詢(xún)緩存,是否存在結(jié)果

        • 結(jié)果不存在,委派給StatementHandler處理器

        StatementHandler:

        • PreparedStatement:處理參數(shù),將參數(shù)賦值到占位符上

          • TypeHandler:具體設(shè)置值的類(lèi)

        • ResultSetHandler:封裝結(jié)果集,封裝成設(shè)置的返回值類(lèi)型

          • TypeHandler:根據(jù)結(jié)果集,取出對(duì)應(yīng)列

        瀏覽 54
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            翔田千里一级无码录像片免费播放 | 两根硕大的挺进他身体里的动态图 | 麻豆电影免费 | 热门视频 - 91爱爱 | 免费操屄网 | 免费无码成人无电影在线观看 | free性满足hd第一次 | 九九成人电影 | 亚洲乱码一区二区三区 | 厨房脱岳裙子在后面挺视频 |