MyBatis執(zhí)行SQL查詢的完整流程
本文將以最常用的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)建事務工廠(JDBC事務工廠,依賴JDBC原生事務管理)
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 3. 構(gòu)建環(huán)境對象(包含環(huán)境名稱、事務工廠、數(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(會話對象,非線程安全,單次請求/事務獨占)
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é)作完成查詢,具體流程可通過以下流程圖及步驟解析理解:

流程步驟詳細解析:
- 獲取Mapper代理對象:調(diào)用
Configuration.getMapper時,MyBatis通過MapperRegistry掃描已注冊的Mapper接口,利用JDK動態(tài)代理生成代理對象,代理對象會攔截所有Mapper方法調(diào)用,將請求轉(zhuǎn)發(fā)至MapperMethod。 - 方法執(zhí)行分發(fā):
MapperMethod.execute作為核心分發(fā)方法,會先解析當前SQL的類型(SELECT/INSERT/UPDATE/DELETE),再根據(jù)類型調(diào)用SqlSession對應的方法(如查詢調(diào)用selectList),同時處理參數(shù)映射(將方法參數(shù)轉(zhuǎn)換為SQL可用參數(shù))。 - SqlSession層轉(zhuǎn)發(fā):
SqlSession作為應用與MyBatis底層的橋梁,不直接執(zhí)行SQL,而是將請求委托給Executor(執(zhí)行器),同時維護事務狀態(tài)。 - Executor執(zhí)行調(diào)度:
Executor是SQL執(zhí)行的核心調(diào)度器,負責事務控制、緩存管理及SQL執(zhí)行協(xié)調(diào)。調(diào)用Executor.query時,會先檢查一級緩存,緩存未命中則繼續(xù)向下執(zhí)行,同時創(chuàng)建Transaction對象管理事務。 - 創(chuàng)建StatementHandler:通過
Configuration.newStatementHandler創(chuàng)建StatementHandler,該組件是JDBCStatement的封裝者,負責Statement的創(chuàng)建、參數(shù)設置、SQL執(zhí)行及結(jié)果處理的統(tǒng)籌。 - 插件攔截增強:
interceptorChain.pluginAll會遍歷所有注冊的攔截器,對StatementHandler進行代理增強(如分頁插件、數(shù)據(jù)脫敏插件可在此處攔截,修改SQL或處理參數(shù)/結(jié)果),這是MyBatis擴展的核心入口。 - 參數(shù)設置:通過
ParameterHandler(參數(shù)處理器)將Mapper方法參數(shù)綁定到JDBCPreparedStatement的占位符上,支持多種參數(shù)類型(基本類型、對象、集合)及參數(shù)映射規(guī)則(如@Param注解、對象屬性映射)。 - SQL執(zhí)行:
StatementHandler.query調(diào)用JDBCStatement的executeQuery方法執(zhí)行SQL,獲取數(shù)據(jù)庫返回的ResultSet。 - 結(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)度,同時處理事務控制、一級緩存管理,其實現(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ù)設置、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、事務等核心組件,提供增刪改查方法及事務控制(提交/回滾)。默認實現(xiàn)為DefaultSqlSession,非線程安全,需保證單次請求/事務對應一個SqlSession,使用后及時關(guān)閉。
5. InterceptorChain(攔截器鏈)
MyBatis的擴展機制核心,管理所有注冊的攔截器(Interceptor),在核心對象創(chuàng)建時(如Executor、StatementHandler),通過pluginAll方法為對象生成代理,允許開發(fā)者在SQL執(zhí)行的關(guān)鍵節(jié)點(參數(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í)行器邏輯(如事務增強、緩存擴展)
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é)作+接口抽象”的設計模式:通過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的核心原理。
以上就是MyBatis執(zhí)行SQL查詢的完整流程的詳細內(nèi)容,更多關(guān)于MyBatis執(zhí)行SQL查詢的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Boot將@RestController誤用于視圖跳轉(zhuǎn)問題解決
這篇文章主要為大家介紹了Spring?Boot將@RestController誤用于視圖跳轉(zhuǎn)問題解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06
詳解Spring Data操作Redis數(shù)據(jù)庫
Redis是一種NOSQL數(shù)據(jù)庫,Key-Value形式對數(shù)據(jù)進行存儲,其中數(shù)據(jù)可以以內(nèi)存形式存在,也可以持久化到文件系統(tǒng)。Spring data對Redis進行了很好的封裝,用起來也是十分的得心應手,接下來通過本文給大家分享Spring Data操作Redis數(shù)據(jù)庫,需要的朋友參考下2017-03-03
前端dist包放到后端springboot項目下一起打包圖文教程
這篇文章主要介紹了前端dist包放到后端springboot項目下一起打包的相關(guān)資料,具體步驟包括前端打包、將前端文件復制到后端項目的static目錄、后端打包、驗證部署成功等,需要的朋友可以參考下2025-01-01
SpringBoot線程池ThreadPoolTaskExecutor異步處理百萬級數(shù)據(jù)
本文主要介紹了SpringBoot線程池ThreadPoolTaskExecutor異步處理百萬級數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-03-03
Java實現(xiàn)JWT 雙簽發(fā)認證+RBAC權(quán)限的示例代碼
本文主要介紹了Java實現(xiàn)JWT 雙簽發(fā)認證+RBAC權(quán)限的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-08-08

