本文将以最常用的select list查询流程为核心,结合核心对象、代码实现与执行链路,全面拆解MyBatis SQL执行的完整过程。
一、select list流程
1. 基本使用(编程式调用示例)
以下代码展示了MyBatis编程式调用Mapper接口执行查询列表的完整示例,涵盖从环境配置、SqlSession创建到结果获取的全步骤,适用于理解底层执行逻辑。
// 1. 获取数据源(模拟配置,实际可通过mybatis-config.xml配置)
DataSource dataSource = TestDataSourceFactory.getDataSource();
// 2. 创建事务工厂(JDBC事务工厂,依赖JDBC原生事务管理)
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 3. 构建环境对象(包含环境名称、事务工厂、数据源)
Environment environment = new Environment("dev", transactionFactory, dataSource);
// 4. 初始化配置对象(MyBatis核心配置载体,存储所有配置信息)
Configuration configuration = new Configuration(environment);
// 5. 注册Mapper接口(告知MyBatis需要扫描的Mapper,绑定SQL映射)
configuration.addMapper(TestMapper.class);
// 6. 构建SqlSessionFactory(会话工厂,线程安全,全局单例)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// 7. 打开SqlSession(会话对象,非线程安全,单次请求/事务独占)
SqlSession session = sqlSessionFactory.openSession();
try {
// 8. 获取Mapper代理对象(MyBatis动态生成,非真实Mapper实现类)
TestMapper mapper = session.getMapper(TestMapper.class);
// 9. 调用Mapper方法执行查询(触发底层SQL执行流程)
List<Entity> entitys = mapper.selectList(101);
} finally {
// 10. 关闭SqlSession(释放资源,避免连接泄漏)
session.close();
}
2. 代码执行流程(完整链路拆解)
上述代码中,mapper.selectList(101)是触发SQL执行的入口,底层通过多组件协作完成查询,具体流程可通过以下流程图及步骤解析理解:
graph TD
A(获取mapper代理对象Configuration.getMapper) --> B(调用代理对象方法MapperMethod.execute)
B --> C(查询SqlSession.selectList)
C --> D(查询Executor.query)
D --> E(获取StatementHandlerConfiguration.newStatementHandler)
E --> F(调用插件返回StatementHandler代理interceptorChain.pluginAll)
F --> G(设置参数ParameterHandler.setParameters)
G --> H(执行sqlStatementHandler.query)
H --> I(处理结果ResultSetHandler.handleResultSets)
流程步骤详细解析:
- 获取Mapper代理对象:调用
Configuration.getMapper时,MyBatis通过MapperRegistry扫描已注册的Mapper接口,利用JDK动态代理生成代理对象,代理对象会拦截所有Mapper方法调用,将请求转发至MapperMethod。 - 方法执行分发:
MapperMethod.execute作为核心分发方法,会先解析当前SQL的类型(SELECT/INSERT/UPDATE/DELETE),再根据类型调用SqlSession对应的方法(如查询调用selectList),同时处理参数映射(将方法参数转换为SQL可用参数)。 - SqlSession层转发:
SqlSession作为应用与MyBatis底层的桥梁,不直接执行SQL,而是将请求委托给Executor(执行器),同时维护事务状态。 - Executor执行调度:
Executor是SQL执行的核心调度器,负责事务控制、缓存管理及SQL执行协调。调用Executor.query时,会先检查一级缓存,缓存未命中则继续向下执行,同时创建Transaction对象管理事务。 - 创建StatementHandler:通过
Configuration.newStatementHandler创建StatementHandler,该组件是JDBCStatement的封装者,负责Statement的创建、参数设置、SQL执行及结果处理的统筹。 - 插件拦截增强:
interceptorChain.pluginAll会遍历所有注册的拦截器,对StatementHandler进行代理增强(如分页插件、数据脱敏插件可在此处拦截,修改SQL或处理参数/结果),这是MyBatis扩展的核心入口。 - 参数设置:通过
ParameterHandler(参数处理器)将Mapper方法参数绑定到JDBCPreparedStatement的占位符上,支持多种参数类型(基本类型、对象、集合)及参数映射规则(如@Param注解、对象属性映射)。 - SQL执行:
StatementHandler.query调用JDBCStatement的executeQuery方法执行SQL,获取数据库返回的ResultSet。 - 结果处理:
ResultSetHandler(结果处理器)将ResultSet数据映射为Java对象(支持简单类型、复杂对象、集合、嵌套结果集等),最终返回给上层调用者。
二、核心对象关系及作用详解
MyBatis SQL执行流程依赖多个核心对象的协作,各对象职责清晰、相互依赖,构成MyBatis的核心架构,以下结合对象功能、实现类及关系展开说明:
1. Configuration(全局配置中心)
MyBatis的“大脑”,存储所有配置信息(全局配置、Mapper映射、插件、类型别名等),同时作为所有核心对象(Executor、StatementHandler、ParameterHandler等)的工厂,负责对象的创建与初始化,贯穿SQL执行全流程。
2. Executor(执行器,SQL执行调度核心)
负责SQL执行的统筹调度,同时处理事务控制、一级缓存管理,其实现类对应不同的执行策略,可通过配置指定默认执行器类型:
- SimpleExecutor(默认执行器):简单执行策略,无任何优化,每执行一条SQL都会新建一个Statement对象,执行完成后关闭,适用于大多数简单场景。
- ReuseExecutor(复用执行器):对相同SQL(SQL语句+参数配置一致)的Statement进行复用,避免频繁创建/关闭Statement,提升性能,适用于重复执行相同SQL的场景。
- BatchExecutor(批处理执行器):实现SQL批处理功能,将多条INSERT/UPDATE/DELETE语句批量提交,减少数据库交互次数,大幅提升批处理效率,仅适用于批处理场景。
- CachingExecutor(缓存执行器):若开启二级缓存,MyBatis会自动为上述执行器包装一层CachingExecutor,负责二级缓存的管理与查询,优先从二级缓存获取数据。
3. StatementHandler(Statement封装者)
直接与JDBC Statement交互,负责Statement的创建、参数设置、SQL执行及结果回调,是MyBatis封装JDBC操作的核心组件,默认实现类对应不同的Statement类型:
- SimpleStatementHandler:处理普通Statement(无占位符的SQL),不支持参数绑定,适用于静态SQL场景。
- PreparedStatementHandler(默认实现):处理PreparedStatement(带占位符的SQL),支持参数绑定、SQL预编译,可防止SQL注入,适用于大多数动态SQL场景。
- CallableStatementHandler:处理CallableStatement,用于执行数据库存储过程,支持输入/输出参数映射。
补充:RoutingStatementHandler并非直接实现类,而是一个路由处理器,会根据SQL类型自动选择对应的StatementHandler实现类。
4. SqlSession(会话对象)
应用程序与MyBatis底层交互的入口,封装了Executor、事务等核心组件,提供增删改查方法及事务控制(提交/回滚)。默认实现为DefaultSqlSession,非线程安全,需保证单次请求/事务对应一个SqlSession,使用后及时关闭。
5. InterceptorChain(拦截器链)
MyBatis的扩展机制核心,管理所有注册的拦截器(Interceptor),在核心对象创建时(如Executor、StatementHandler),通过pluginAll方法为对象生成代理,允许开发者在SQL执行的关键节点(参数设置、SQL执行、结果处理)插入自定义逻辑,常见应用场景包括自动分页、数据脱敏、日志打印、分库分表等。
6. 其他辅助组件
- ParameterHandler:参数处理器,负责将Mapper方法参数绑定到Statement占位符,支持类型转换(如Java类型与数据库类型映射)。
- ResultSetHandler:结果处理器,负责将ResultSet转换为Java对象,支持复杂结果映射(如一对一、一对多嵌套映射)。
- MetaObject:元对象工具,用于操作Java对象的属性(即使属性为private),支撑参数绑定与结果映射的底层反射操作。
三、核心代码逻辑简述
以下通过核心类的关键代码,进一步理解MyBatis对象创建与SQL执行的底层逻辑,重点解析Configuration(对象工厂)与MapperMethod(方法分发)的核心实现。
1. org.apache.ibatis.session.Configuration(核心对象工厂)
该类不仅存储配置,更通过一系列newXXX方法创建核心对象,同时集成拦截器链,为对象提供增强能力,关键代码解析如下:
public class Configuration {
/**
* 获取Mapper代理对象
* 核心逻辑:委托MapperRegistry创建代理,将SqlSession传入代理,便于后续调用
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
/**
* 创建元对象,用于反射操作对象属性
* 支撑ParameterHandler、ResultSetHandler的底层属性访问
*/
public MetaObject newMetaObject(Object object) {
return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
}
/**
* 创建ParameterHandler(参数处理器)
* 先通过语言驱动创建默认实现,再通过拦截器链增强
*/
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 拦截器增强:允许插件修改参数处理逻辑
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
/**
* 创建ResultSetHandler(结果处理器)
* 实例化默认实现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);
// 拦截器增强:允许插件修改结果处理逻辑(如数据脱敏)
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
/**
* 创建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执行逻辑(如分页插件)
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
/**
* 创建Executor(执行器)
* 根据执行器类型实例化对应实现,开启缓存则包装为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);
}
// 拦截器增强:允许插件修改执行器逻辑(如事务增强、缓存扩展)
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}
2. org.apache.ibatis.binding.MapperMethod(方法执行分发器)
该类负责解析Mapper方法的SQL类型、参数信息,将方法调用分发至SqlSession对应的方法,是Mapper代理对象与SqlSession之间的桥梁,核心execute方法逻辑如下(补充完整核心逻辑):
public class MapperMethod {
// 存储SQL命令信息(类型、语句ID等)
private final SqlCommand command;
// 存储方法签名信息(返回值类型、参数类型等)
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);
}
// 核心方法:处理增删改查方法执行,分发至SqlSession
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
// 根据SQL命令类型分发执行
switch (command.getType()) {
case INSERT: {
// 处理参数映射,将args转换为SQL参数
Object param = method.convertArgsToSqlCommandParam(args);
// 调用SqlSession.insert,返回影响行数
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参数(回调处理结果)
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
}
// 返回列表/数组
else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
}
// 返回Map
else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
}
// 返回Cursor(游标,适用于大量数据流式查询)
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;
}
// 执行查询列表逻辑
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
// 若方法有RowBounds参数(分页参数),调用带分页的selectList
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
// 无分页,调用普通selectList
result = sqlSession.selectList(command.getName(), param);
}
// 若返回值需要转换为数组,进行类型转换
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
return method.convertResultToList(result);
}
return result;
}
// 其他辅助方法(executeWithResultHandler、executeForMap等)省略...
// 处理影响行数返回结果(适配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;
}
}
四、总结
MyBatis SQL执行流程本质是“分层协作+接口抽象”的设计模式:通过Configuration统一管理配置与对象创建,SqlSession提供上层入口,Executor调度核心流程,StatementHandler封装JDBC操作,配合ParameterHandler、ResultSetHandler完成参数与结果的映射,最终通过拦截器链提供灵活扩展能力。
理解这一流程,既能帮助开发者快速定位SQL执行过程中的问题(如参数绑定错误、结果映射异常、插件拦截失效等),也能为自定义扩展(如开发插件、优化执行逻辑)提供底层支撑,真正掌握MyBatis的核心原理。