MyBatis作为Java生态中主流的持久层框架,以“轻量级、高灵活、低侵入”的特性被广泛应用。其核心价值在于简化JDBC操作、解耦SQL与业务代码,同时提供强大的结果映射与动态SQL能力。很多开发者熟练使用MyBatis,但对其底层执行流程却一知半解。本文将从源码视角出发,逐层拆解MyBatis从初始化到SQL执行、结果返回的完整流程,结合实例剖析核心组件的协同逻辑,帮你吃透框架设计精髓,实现“知其然更知其所以然”。
一、核心前提:MyBatis核心组件与初始化流程
MyBatis的执行流程依赖一套精心设计的核心组件,这些组件在初始化阶段完成加载与装配,为后续SQL执行奠定基础。在分析执行流程前,先明确核心组件及初始化逻辑,避免后续理解断层。
1. 核心组件清单
MyBatis的执行链路涉及多个组件的协同工作,每个组件承担特定职责,缺一不可:
-
SqlSessionFactory:SQL会话工厂,MyBatis的核心入口,负责创建SqlSession,本质是对配置信息的封装,生命周期为应用级(全局单例)。
-
SqlSession:SQL会话,代表与数据库的一次交互会话,提供CRUD操作的API,生命周期为请求级(线程私有,不可共享)。
-
Executor:执行器,MyBatis的核心执行组件,负责SQL语句的执行、缓存管理、事务控制,SqlSession的所有操作最终委托给Executor。
-
MappedStatement:映射语句,封装SQL配置信息(SQL语句、参数类型、结果类型、执行方式等),对应mapper.xml中的一个<select>/<insert>/<update>/<delete>标签或@Select等注解。
-
ParameterHandler:参数处理器,负责将Java参数转换为JDBC语句的参数(设置PreparedStatement的参数值),处理参数类型匹配、类型转换等逻辑。
-
ResultSetHandler:结果集处理器,负责将JDBC查询结果集转换为Java对象,实现结果映射(ResultMap配置的解析与应用)。
-
StatementHandler:语句处理器,负责创建JDBC的Statement/PreparedStatement/CallableStatement对象,与数据库交互执行SQL,协调ParameterHandler和ResultSetHandler的工作。
-
Configuration:全局配置对象,存储MyBatis所有配置信息(核心配置文件、mapper映射、插件、类型别名等),是组件初始化的核心依据。
2. 初始化核心流程(SqlSessionFactory创建)
MyBatis的执行流程始于初始化,核心是构建SqlSessionFactory,该过程本质是加载配置信息并初始化Configuration对象,具体步骤如下:
加载配置资源:通过Resources工具类加载mybatis-config.xml核心配置文件(或读取注解配置、Java配置),获取输入流。
创建SqlSessionFactoryBuilder:通过构建者模式(Builder)创建SqlSessionFactoryBuilder对象,其核心职责是解析配置并构建SqlSessionFactory。
解析配置并初始化Configuration:SqlSessionFactoryBuilder调用build()方法,解析配置文件中的<configuration>标签,依次加载属性(properties)、设置(settings)、类型别名(typeAliases)、插件(plugins)、环境(environments)、mapper映射等信息,封装到Configuration对象中。
创建SqlSessionFactory:基于初始化完成的Configuration对象,创建DefaultSqlSessionFactory(SqlSessionFactory的默认实现类),至此初始化完成。
核心代码示例(经典XML配置方式):
// 1. 加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 2. 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 打开SqlSession(开启会话,进入执行流程)
SqlSession sqlSession = sqlSessionFactory.openSession();
二、全链路拆解:MyBatis SQL执行核心流程
从SqlSession获取到结果返回,MyBatis的SQL执行流程可分为7个核心步骤,各步骤环环相扣,涉及核心组件的协同工作。以下以最常用的查询操作(select)为例,结合源码与实例深度剖析。
步骤1:获取SqlSession,开启数据库会话
通过SqlSessionFactory的openSession()方法获取SqlSession,该方法会完成Executor的创建、事务的初始化,同时绑定线程上下文(确保线程安全)。
openSession()方法的核心逻辑(DefaultSqlSessionFactory源码简化):
@Override
public SqlSession openSession() {
// 根据配置获取Executor类型(SIMPLE/REUSE/BATCH),创建Executor
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境配置中的数据源和事务工厂
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor(执行器),并应用插件拦截
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession(SqlSession默认实现)
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // 异常时关闭事务
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
}
}
关键说明:Executor的类型由mybatis-config.xml中的<setting name="defaultExecutorType" value="SIMPLE"/>配置,默认SIMPLE(简单执行器,每次执行SQL创建新的Statement),REUSE(可重用执行器,复用Statement),BATCH(批处理执行器,适用于批量插入/更新)。
步骤2:获取Mapper接口代理对象
MyBatis不直接操作Mapper接口,而是通过动态代理机制生成Mapper代理对象,将SQL执行请求委托给SqlSession。开发者通过SqlSession的getMapper()方法获取代理对象,核心是基于JDK动态代理实现。
代码示例与源码逻辑:
// 获取Mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 调用代理方法执行SQL
User user = userMapper.selectById(1L);
getMapper()方法的核心是通过MapperProxyFactory创建MapperProxy(代理类),MapperProxy实现InvocationHandler接口,拦截Mapper接口的所有方法调用,将方法映射为对应的MappedStatement,触发后续SQL执行。
步骤3:解析方法,获取MappedStatement
当调用Mapper代理对象的方法(如selectById)时,MapperProxy会拦截该调用,通过“接口全限定名+方法名”的组合键(如com.example.mapper.UserMapper.selectById),从Configuration中获取对应的MappedStatement对象。
核心逻辑(MapperProxy源码简化):
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 处理Object类的方法(如toString、hashCode)
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 执行Mapper方法,获取MappedStatement并执行
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
MappedStatement是SQL配置的核心封装,包含了SQL语句、参数映射(ParameterMap)、结果映射(ResultMap)、执行类型(STATEMENT/PREPARED/CALLABLE)等关键信息,是后续SQL执行的依据。
步骤4:Executor调用StatementHandler,准备SQL执行
SqlSession将SQL执行请求委托给Executor,Executor根据MappedStatement的配置,创建对应的StatementHandler(语句处理器),并协调ParameterHandler完成参数设置。
核心流程(以SimpleExecutor为例,源码简化):
@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();
// 创建StatementHandler,应用插件拦截
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备Statement(创建并设置参数)
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询,返回结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
// 准备Statement:创建Statement + 设置参数
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 创建JDBC Statement/PreparedStatement
stmt = handler.prepare(connection, transaction.getTimeout());
// 调用ParameterHandler设置参数
handler.parameterize(stmt);
return stmt;
}
关键说明:StatementHandler的类型由MappedStatement的执行类型决定,默认PREPARED(对应JDBC的PreparedStatement,支持参数占位符,防止SQL注入),STATEMENT(对应Statement,直接执行静态SQL),CALLABLE(对应CallableStatement,调用存储过程)。
步骤5:ParameterHandler处理参数,绑定到Statement
ParameterHandler的核心职责是将Java参数转换为JDBC Statement的参数,通过setParameters()方法完成参数绑定,支持基本类型、包装类、自定义对象、集合等参数类型,同时处理类型转换(基于MyBatis的TypeHandler机制)。
DefaultParameterHandler(ParameterHandler默认实现)源码简化:
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
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) {
Object value;
String propertyName = parameterMapping.getProperty();
// 获取参数值(支持单个参数、Map参数、自定义对象参数)
if (boundSql.hasAdditionalParameter(propertyName)) {
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 = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
这里的TypeHandler(类型处理器)是MyBatis参数转换的核心,支持Java类型与JDBC类型的映射(如Integer→INTEGER、String→VARCHAR),也可自定义TypeHandler处理特殊类型(如LocalDateTime→TIMESTAMP)。
步骤6:执行SQL,ResultSetHandler处理结果映射
参数绑定完成后,StatementHandler调用Statement的execute()方法执行SQL(查询用executeQuery(),增删改用executeUpdate()),获取JDBC ResultSet(查询结果集),再由ResultSetHandler将结果集转换为Java对象,实现结果映射。
ResultSetHandler核心逻辑(DefaultResultSetHandler源码简化):
@Override
public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<ResultSet> resultSets = new ArrayList<>();
// 获取执行结果集(支持多结果集,如存储过程返回多个结果)
ResultSet rs = stmt.getResultSet();
while (rs != null) {
resultSets.add(rs);
// 处理游标,获取下一个结果集
rs = stmt.getMoreResults();
if (rs != null && stmt.getUpdateCount() == -1) {
rs = stmt.getResultSet();
}
}
// 从MappedStatement获取结果映射配置
final List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 校验结果映射配置
if (resultMapCount == 0) {
return Collections.emptyList();
}
// 处理结果集,转换为Java对象
return handleResultSet(rs, resultMaps.get(0), null, null);
}
结果映射(ResultMap)是MyBatis的核心特性之一,ResultSetHandler会根据ResultMap的配置,将ResultSet中的列值映射到Java对象的属性上,支持:
-
自动映射:当列名与Java对象属性名一致时,自动赋值(可通过<setting name="autoMappingBehavior" value="PARTIAL"/>配置);
-
手动映射:通过<resultMap>标签配置列名与属性名的映射关系,支持复杂类型(如一对一、一对多关联查询);
-
类型转换:通过TypeHandler将JDBC类型转换为Java类型(与参数处理的TypeHandler复用)。
步骤7:关闭资源,提交/回滚事务
SQL执行完成并获取结果后,需手动关闭SqlSession(或通过Spring集成由容器管理),SqlSession关闭时会触发事务的提交/回滚(根据是否自动提交配置)、Executor关闭、Statement关闭、Connection归还到数据源等操作。
核心代码示例与逻辑:
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(1L);
// 手动提交事务(非自动提交模式下)
sqlSession.commit();
} catch (Exception e) {
// 异常回滚事务
sqlSession.rollback();
} finally {
// 关闭SqlSession,释放资源
sqlSession.close();
}
SqlSession的close()方法会最终调用Executor的close()方法,关闭相关资源,确保数据库连接及时归还,避免连接泄漏。
三、补充:MyBatis缓存机制与插件拦截
在核心执行流程之外,MyBatis的缓存机制与插件拦截器会对执行流程产生影响,是提升性能与扩展功能的关键,需结合流程理解。
1. 缓存机制的介入时机
MyBatis提供一级缓存(SqlSession级别,默认开启)和二级缓存(SqlSessionFactory级别,需手动开启),缓存查询结果以减少数据库交互:
-
一级缓存:在Executor执行查询前,会先从本地缓存(PerpetualCache)中查询,若存在则直接返回结果,无需执行SQL;若不存在则执行SQL,查询结果存入一级缓存。一级缓存随SqlSession关闭而失效。
-
二级缓存:开启后,查询结果会先存入二级缓存,多个SqlSession可共享二级缓存。执行增删改操作时,会清空对应缓存,保证数据一致性。
2. 插件拦截器的执行节点
MyBatis支持通过插件拦截核心组件的方法,实现功能扩展(如分页、日志、权限控制)。插件可拦截的核心组件及方法包括:
-
Executor:update、query、commit、rollback等方法;
-
StatementHandler:prepare、parameterize、query、update等方法;
-
ParameterHandler:setParameters方法;
-
ResultSetHandler:handleResultSets、handleOutputParameters等方法。
插件通过动态代理机制介入执行流程,在目标方法执行前后添加自定义逻辑,是MyBatis高扩展性的核心体现。
四、总结:MyBatis执行流程的核心逻辑闭环
MyBatis的执行流程本质是“配置驱动+组件协同+代理机制”的结合,形成了从初始化到资源释放的完整闭环:
初始化阶段:加载配置文件,构建SqlSessionFactory,初始化Configuration与核心组件,为执行流程提供基础;
会话阶段:通过SqlSessionFactory创建SqlSession,绑定Executor与事务,开启数据库会话;
执行阶段:通过Mapper动态代理拦截方法,解析获取MappedStatement,由Executor协调StatementHandler、ParameterHandler、ResultSetHandler完成SQL执行、参数绑定、结果映射;
收尾阶段:关闭SqlSession,释放资源,提交/回滚事务,确保流程完整性。
理解MyBatis的执行流程,不仅能帮助开发者快速定位SQL执行中的问题(如参数绑定错误、结果映射失败、缓存不一致等),更能在复杂场景下(如自定义插件、优化性能)灵活运用框架特性。MyBatis的设计精髓在于“各司其职”的组件划分与“低耦合高扩展”的架构,这也是其能在持久层框架中占据一席之地的核心原因。
后续可结合Spring与MyBatis的集成逻辑(如SqlSessionTemplate、MapperScannerConfigurer),进一步理解企业级开发中的执行流程优化,让技术能力再上一个台阶。



