最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis SQL執(zhí)行流程的使用詳解

 更新時間:2026年02月28日 10:51:55   作者:程序員越  
本文主要介紹了MyBatis SQL執(zhí)行流程的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

本文將以最常用的select list查詢流程為核心,結(jié)合核心對象、代碼實現(xiàn)與執(zhí)行鏈路,全面拆解MyBatis SQL執(zhí)行的完整過程。

一、select list流程

1. 基本使用(編程式調(diào)用示例)

以下代碼展示了MyBatis編程式調(diào)用Mapper接口執(zhí)行查詢列表的完整示例,涵蓋從環(huán)境配置、SqlSession創(chuàng)建到結(jié)果獲取的全步驟,適用于理解底層執(zhí)行邏輯。

// 1. 獲取數(shù)據(jù)源(模擬配置,實際可通過mybatis-config.xml配置)
DataSource dataSource = TestDataSourceFactory.getDataSource();
// 2. 創(chuàng)建事務(wù)工廠(JDBC事務(wù)工廠,依賴JDBC原生事務(wù)管理)
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 3. 構(gòu)建環(huán)境對象(包含環(huán)境名稱、事務(wù)工廠、數(shù)據(jù)源)
Environment environment = new Environment("dev", transactionFactory, dataSource);
// 4. 初始化配置對象(MyBatis核心配置載體,存儲所有配置信息)
Configuration configuration = new Configuration(environment);
// 5. 注冊Mapper接口(告知MyBatis需要掃描的Mapper,綁定SQL映射)
configuration.addMapper(TestMapper.class);
// 6. 構(gòu)建SqlSessionFactory(會話工廠,線程安全,全局單例)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// 7. 打開SqlSession(會話對象,非線程安全,單次請求/事務(wù)獨占)
SqlSession session = sqlSessionFactory.openSession();
try { 
  // 8. 獲取Mapper代理對象(MyBatis動態(tài)生成,非真實Mapper實現(xiàn)類)
  TestMapper mapper = session.getMapper(TestMapper.class);
  // 9. 調(diào)用Mapper方法執(zhí)行查詢(觸發(fā)底層SQL執(zhí)行流程)
  List<Entity> entitys = mapper.selectList(101);
} finally {
  // 10. 關(guān)閉SqlSession(釋放資源,避免連接泄漏)
  session.close();
}

2. 代碼執(zhí)行流程(完整鏈路拆解)

上述代碼中,mapper.selectList(101)是觸發(fā)SQL執(zhí)行的入口,底層通過多組件協(xié)作完成查詢,具體流程可通過以下流程圖及步驟解析理解:

流程步驟詳細解析:

  1. 獲取Mapper代理對象:調(diào)用Configuration.getMapper時,MyBatis通過MapperRegistry掃描已注冊的Mapper接口,利用JDK動態(tài)代理生成代理對象,代理對象會攔截所有Mapper方法調(diào)用,將請求轉(zhuǎn)發(fā)至MapperMethod。
  2. 方法執(zhí)行分發(fā):MapperMethod.execute作為核心分發(fā)方法,會先解析當前SQL的類型(SELECT/INSERT/UPDATE/DELETE),再根據(jù)類型調(diào)用SqlSession對應的方法(如查詢調(diào)用selectList),同時處理參數(shù)映射(將方法參數(shù)轉(zhuǎn)換為SQL可用參數(shù))。
  3. SqlSession層轉(zhuǎn)發(fā):SqlSession作為應用與MyBatis底層的橋梁,不直接執(zhí)行SQL,而是將請求委托給Executor(執(zhí)行器),同時維護事務(wù)狀態(tài)。
  4. Executor執(zhí)行調(diào)度:Executor是SQL執(zhí)行的核心調(diào)度器,負責事務(wù)控制、緩存管理及SQL執(zhí)行協(xié)調(diào)。調(diào)用Executor.query時,會先檢查一級緩存,緩存未命中則繼續(xù)向下執(zhí)行,同時創(chuàng)建Transaction對象管理事務(wù)。
  5. 創(chuàng)建StatementHandler:通過Configuration.newStatementHandler創(chuàng)建StatementHandler,該組件是JDBC Statement的封裝者,負責Statement的創(chuàng)建、參數(shù)設(shè)置、SQL執(zhí)行及結(jié)果處理的統(tǒng)籌。
  6. 插件攔截增強:interceptorChain.pluginAll會遍歷所有注冊的攔截器,對StatementHandler進行代理增強(如分頁插件、數(shù)據(jù)脫敏插件可在此處攔截,修改SQL或處理參數(shù)/結(jié)果),這是MyBatis擴展的核心入口。
  7. 參數(shù)設(shè)置:通過ParameterHandler(參數(shù)處理器)將Mapper方法參數(shù)綁定到JDBC PreparedStatement的占位符上,支持多種參數(shù)類型(基本類型、對象、集合)及參數(shù)映射規(guī)則(如@Param注解、對象屬性映射)。
  8. SQL執(zhí)行:StatementHandler.query調(diào)用JDBC Statement的executeQuery方法執(zhí)行SQL,獲取數(shù)據(jù)庫返回的ResultSet。
  9. 結(jié)果處理:ResultSetHandler(結(jié)果處理器)將ResultSet數(shù)據(jù)映射為Java對象(支持簡單類型、復雜對象、集合、嵌套結(jié)果集等),最終返回給上層調(diào)用者。

二、核心對象關(guān)系及作用詳解

MyBatis SQL執(zhí)行流程依賴多個核心對象的協(xié)作,各對象職責清晰、相互依賴,構(gòu)成MyBatis的核心架構(gòu),以下結(jié)合對象功能、實現(xiàn)類及關(guān)系展開說明:

1. Configuration(全局配置中心)

MyBatis的“大腦”,存儲所有配置信息(全局配置、Mapper映射、插件、類型別名等),同時作為所有核心對象(Executor、StatementHandler、ParameterHandler等)的工廠,負責對象的創(chuàng)建與初始化,貫穿SQL執(zhí)行全流程。

2. Executor(執(zhí)行器,SQL執(zhí)行調(diào)度核心)

負責SQL執(zhí)行的統(tǒng)籌調(diào)度,同時處理事務(wù)控制、一級緩存管理,其實現(xiàn)類對應不同的執(zhí)行策略,可通過配置指定默認執(zhí)行器類型:

  • SimpleExecutor(默認執(zhí)行器):簡單執(zhí)行策略,無任何優(yōu)化,每執(zhí)行一條SQL都會新建一個Statement對象,執(zhí)行完成后關(guān)閉,適用于大多數(shù)簡單場景。
  • ReuseExecutor(復用執(zhí)行器):對相同SQL(SQL語句+參數(shù)配置一致)的Statement進行復用,避免頻繁創(chuàng)建/關(guān)閉Statement,提升性能,適用于重復執(zhí)行相同SQL的場景。
  • BatchExecutor(批處理執(zhí)行器):實現(xiàn)SQL批處理功能,將多條INSERT/UPDATE/DELETE語句批量提交,減少數(shù)據(jù)庫交互次數(shù),大幅提升批處理效率,僅適用于批處理場景。
  • CachingExecutor(緩存執(zhí)行器):若開啟二級緩存,MyBatis會自動為上述執(zhí)行器包裝一層CachingExecutor,負責二級緩存的管理與查詢,優(yōu)先從二級緩存獲取數(shù)據(jù)。

3. StatementHandler(Statement封裝者)

直接與JDBC Statement交互,負責Statement的創(chuàng)建、參數(shù)設(shè)置、SQL執(zhí)行及結(jié)果回調(diào),是MyBatis封裝JDBC操作的核心組件,默認實現(xiàn)類對應不同的Statement類型:

  • SimpleStatementHandler:處理普通Statement(無占位符的SQL),不支持參數(shù)綁定,適用于靜態(tài)SQL場景。
  • PreparedStatementHandler(默認實現(xiàn)):處理PreparedStatement(帶占位符的SQL),支持參數(shù)綁定、SQL預編譯,可防止SQL注入,適用于大多數(shù)動態(tài)SQL場景。
  • CallableStatementHandler:處理CallableStatement,用于執(zhí)行數(shù)據(jù)庫存儲過程,支持輸入/輸出參數(shù)映射。

補充:RoutingStatementHandler并非直接實現(xiàn)類,而是一個路由處理器,會根據(jù)SQL類型自動選擇對應的StatementHandler實現(xiàn)類。

4. SqlSession(會話對象)

應用程序與MyBatis底層交互的入口,封裝了Executor、事務(wù)等核心組件,提供增刪改查方法及事務(wù)控制(提交/回滾)。默認實現(xiàn)為DefaultSqlSession,非線程安全,需保證單次請求/事務(wù)對應一個SqlSession,使用后及時關(guān)閉。

5. InterceptorChain(攔截器鏈)

MyBatis的擴展機制核心,管理所有注冊的攔截器(Interceptor),在核心對象創(chuàng)建時(如Executor、StatementHandler),通過pluginAll方法為對象生成代理,允許開發(fā)者在SQL執(zhí)行的關(guān)鍵節(jié)點(參數(shù)設(shè)置、SQL執(zhí)行、結(jié)果處理)插入自定義邏輯,常見應用場景包括自動分頁、數(shù)據(jù)脫敏、日志打印、分庫分表等。

6. 其他輔助組件

  • ParameterHandler:參數(shù)處理器,負責將Mapper方法參數(shù)綁定到Statement占位符,支持類型轉(zhuǎn)換(如Java類型與數(shù)據(jù)庫類型映射)。
  • ResultSetHandler:結(jié)果處理器,負責將ResultSet轉(zhuǎn)換為Java對象,支持復雜結(jié)果映射(如一對一、一對多嵌套映射)。
  • MetaObject:元對象工具,用于操作Java對象的屬性(即使屬性為private),支撐參數(shù)綁定與結(jié)果映射的底層反射操作。

三、核心代碼邏輯簡述

以下通過核心類的關(guān)鍵代碼,進一步理解MyBatis對象創(chuàng)建與SQL執(zhí)行的底層邏輯,重點解析Configuration(對象工廠)與MapperMethod(方法分發(fā))的核心實現(xiàn)。

1. org.apache.ibatis.session.Configuration(核心對象工廠)

該類不僅存儲配置,更通過一系列newXXX方法創(chuàng)建核心對象,同時集成攔截器鏈,為對象提供增強能力,關(guān)鍵代碼解析如下:

public class Configuration {
   /**
    * 獲取Mapper代理對象
    * 核心邏輯:委托MapperRegistry創(chuàng)建代理,將SqlSession傳入代理,便于后續(xù)調(diào)用
    */
   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	    return mapperRegistry.getMapper(type, sqlSession);
	}

	/**
	 * 創(chuàng)建元對象,用于反射操作對象屬性
	 * 支撐ParameterHandler、ResultSetHandler的底層屬性訪問
	 */
	public MetaObject newMetaObject(Object object) {
	    return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
	}

	/**
	 * 創(chuàng)建ParameterHandler(參數(shù)處理器)
	 * 先通過語言驅(qū)動創(chuàng)建默認實現(xiàn),再通過攔截器鏈增強
	 */
	public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
	    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
        // 攔截器增強:允許插件修改參數(shù)處理邏輯
	    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
	    return parameterHandler;
	}

	/**
	 * 創(chuàng)建ResultSetHandler(結(jié)果處理器)
	 * 實例化默認實現(xiàn)DefaultResultSetHandler,再通過攔截器鏈增強
	 */
	public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
	      ResultHandler resultHandler, BoundSql boundSql) {
	    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
        // 攔截器增強:允許插件修改結(jié)果處理邏輯(如數(shù)據(jù)脫敏)
	    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
	    return resultSetHandler;
	}

	/**
	 * 創(chuàng)建StatementHandler(Statement封裝者)
	 * 實例化路由處理器RoutingStatementHandler,再通過攔截器鏈增強
	 */
	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);
        // 攔截器增強:允許插件修改SQL執(zhí)行邏輯(如分頁插件)
	    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
	    return statementHandler;
	}

	/**
	 * 創(chuàng)建Executor(執(zhí)行器)
	 * 根據(jù)執(zhí)行器類型實例化對應實現(xiàn),開啟緩存則包裝為CachingExecutor,最后通過攔截器增強
	 */
	public Executor newExecutor(Transaction transaction) {
	    return newExecutor(transaction, defaultExecutorType);
	}

	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);
	    }
	    // 若開啟二級緩存,包裝為CachingExecutor
	    if (cacheEnabled) {
	      executor = new CachingExecutor(executor);
	    }
        // 攔截器增強:允許插件修改執(zhí)行器邏輯(如事務(wù)增強、緩存擴展)
	    executor = (Executor) interceptorChain.pluginAll(executor);
	    return executor;
	}
}

2. org.apache.ibatis.binding.MapperMethod(方法執(zhí)行分發(fā)器)

該類負責解析Mapper方法的SQL類型、參數(shù)信息,將方法調(diào)用分發(fā)至SqlSession對應的方法,是Mapper代理對象與SqlSession之間的橋梁,核心execute方法邏輯如下(補充完整核心邏輯):

public class MapperMethod {
  // 存儲SQL命令信息(類型、語句ID等)
  private final SqlCommand command;
  // 存儲方法簽名信息(返回值類型、參數(shù)類型等)
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  // 核心方法:處理增刪改查方法執(zhí)行,分發(fā)至SqlSession
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    // 根據(jù)SQL命令類型分發(fā)執(zhí)行
    switch (command.getType()) {
      case INSERT: {
        // 處理參數(shù)映射,將args轉(zhuǎn)換為SQL參數(shù)
        Object param = method.convertArgsToSqlCommandParam(args);
        // 調(diào)用SqlSession.insert,返回影響行數(shù)
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        // 無返回值且有ResultHandler參數(shù)(回調(diào)處理結(jié)果)
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        }
        // 返回列表/數(shù)組
        else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        }
        // 返回Map
        else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        }
        // 返回Cursor(游標,適用于大量數(shù)據(jù)流式查詢)
        else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        }
        // 返回單個對象
        else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    // 處理返回值為null且方法返回類型為基本類型的情況(拋出異常)
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

  // 執(zhí)行查詢列表邏輯
  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    // 若方法有RowBounds參數(shù)(分頁參數(shù)),調(diào)用帶分頁的selectList
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      // 無分頁,調(diào)用普通selectList
      result = sqlSession.selectList(command.getName(), param);
    }
    // 若返回值需要轉(zhuǎn)換為數(shù)組,進行類型轉(zhuǎn)換
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      return method.convertResultToList(result);
    }
    return result;
  }

  // 其他輔助方法(executeWithResultHandler、executeForMap等)省略...

  // 處理影響行數(shù)返回結(jié)果(適配boolean/int等返回類型)
  private Object rowCountResult(int rowCount) {
    final Object result;
    if (method.returnsVoid()) {
      result = null;
    } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
      result = rowCount;
    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
      result = (long) rowCount;
    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
      result = rowCount > 0;
    } else {
      throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
    }
    return result;
  }
}

四、總結(jié)

MyBatis SQL執(zhí)行流程本質(zhì)是“分層協(xié)作+接口抽象”的設(shè)計模式:通過Configuration統(tǒng)一管理配置與對象創(chuàng)建,SqlSession提供上層入口,Executor調(diào)度核心流程,StatementHandler封裝JDBC操作,配合ParameterHandler、ResultSetHandler完成參數(shù)與結(jié)果的映射,最終通過攔截器鏈提供靈活擴展能力。

理解這一流程,既能幫助開發(fā)者快速定位SQL執(zhí)行過程中的問題(如參數(shù)綁定錯誤、結(jié)果映射異常、插件攔截失效等),也能為自定義擴展(如開發(fā)插件、優(yōu)化執(zhí)行邏輯)提供底層支撐,真正掌握MyBatis的核心原理。

到此這篇關(guān)于MyBatis SQL執(zhí)行流程的使用詳解的文章就介紹到這了,更多相關(guān)MyBatis SQL執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

库尔勒市| 禹城市| 太康县| 安西县| 大宁县| 海盐县| 札达县| 奉化市| 方城县| 舟山市| 丽水市| 天门市| 辽阳县| 苍梧县| 屏山县| 建阳市| 诸城市| 邢台市| 富顺县| 大竹县| 镇平县| 兴隆县| 永兴县| 鸡泽县| 英山县| 通渭县| 阿图什市| 清苑县| 什邡市| 财经| 西吉县| 庆安县| 淮滨县| 汝城县| 旌德县| 九龙县| 从化市| 开阳县| 富裕县| 四平市| 邢台市|