Unveiling MyBatis: How Mapper Binding and SQL Execution Work Internally
This article dissects MyBatis 3.5.5’s internal workflow, explaining how mapper interfaces bind to XML files, the step‑by‑step SQL execution process, custom typeHandler creation for parameter and result mapping, and the underlying proxy and configuration mechanisms that drive query handling.
Introduction
MyBatis is widely used, yet many developers are unclear about its internal SQL execution flow. This article covers four key topics: how mapper interfaces and XML files are bound, the complete SQL execution process, custom parameter handling via typeHandler, and custom result set handling.
Overview
A typical programmatic query in MyBatis looks like the following code:
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<LwUser> userList = userMapper.listUserByUserName("孤狼1号");The first line obtains a SqlSession object, the second retrieves the mapper proxy, and the third executes the query. The following sections analyse the second and third steps in depth.
Getting the Mapper Interface (getMapper)
When session.getMapper is called, MyBatis follows a runtime sequence illustrated below:
Configuration retrieves the Mapper object because all mapper interfaces are loaded and stored during application startup.
MapperRegistry inside Configuration is consulted to obtain the mapper factory.
The factory creates a JDK dynamic proxy ( MapperProxy) that implements the mapper interface.
The proxy implements InvocationHandler, enabling method interception.
When Mapper Interface and XML Are Bound
During the parsing of mybatis-config.xml, MyBatis loads mapper interfaces and their corresponding XML files. The process includes:
Calling SqlSessionFactoryBuilder.build() to create an XMLConfigBuilder.
Parsing the configuration file and the <mappers> node.
Depending on the configuration style, either the XML file is parsed directly or the mapper interface is registered first and the XML is parsed later.
Finally, Configuration.addMapper stores the mapper in knownMappers, completing the binding.
SQL Execution Process
The execution can be divided into two major steps: locating the SQL statement and executing it.
Locating the SQL
The proxy’s invoke method creates a MapperMethod object that encapsulates the method’s metadata and the associated SQL. The sequence diagram below shows this flow.
Executing the SQL
After the MapperMethod is prepared, MyBatis delegates to Executor.execute. Depending on the return type, it may call executeForMany for collections. The flow continues through StatementHandler.prepare, StatementHandler.parameterize, and finally PreparedStatement.execute. The following diagram illustrates the execution path.
Parameter Mapping
Parameter setting is performed by BaseTypeHandler subclasses. MyBatis provides default handlers (e.g., StringTypeHandler, IntTypeHandler) that call the appropriate PreparedStatement methods. Developers can create custom handlers by extending BaseTypeHandler:
package com.lonelyWolf.mybatis.typeHandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class MyTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
System.out.println("Custom typeHandler applied");
ps.setString(i, parameter);
}
// getNullableResult methods omitted for brevity
}Using the custom handler in a mapper XML:
<select id="listUserByUserName" parameterType="String" resultMap="MyUserResultMap">
select user_id, user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR,typeHandler=com.lonelyWolf.mybatis.typeHandler.MyTypeHandler}
</select>Running the query prints the custom handler’s log, confirming it is invoked.
Result Set Mapping
Result handling follows a similar pattern. After the JDBC ResultSet is obtained, MyBatis iterates through the rows and uses the configured typeHandler to extract column values. Custom result handlers can also be defined by overriding the getNullableResult methods in BaseTypeHandler.
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("Getting result via columnName – custom handler");
return rs.getString(columnName);
}The corresponding <resultMap> entry specifies the custom handler for a column:
<resultMap id="MyUserResultMap" type="lwUser">
<result column="user_id" property="userId" jdbcType="VARCHAR" typeHandler="com.lonelyWolf.mybatis.typeHandler.MyTypeHandler"/>
<result column="user_name" property="userName" jdbcType="VARCHAR"/>
</resultMap>Overall Workflow Diagram
The diagram below summarises the main objects involved in a MyBatis query (SqlSession, Configuration, MapperRegistry, Executor, StatementHandler, ResultSetHandler, etc.).
Conclusion
This article analysed MyBatis’s internal SQL execution workflow, demonstrated how mapper interfaces bind to XML files, and showed how to customise parameter and result mapping with user‑defined typeHandler s. While the default handlers satisfy most scenarios, custom handlers provide a powerful extension point for special requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
