Unveiling MyBatis: How Mapper Interfaces Bind and SQL Executes Internally
This article thoroughly dissects MyBatis’s internal workflow, explaining how mapper interfaces are linked to XML files, detailing the step‑by‑step SQL execution process, and demonstrating custom typeHandler creation for both parameter and result mapping, all based on MyBatis 3.5.5 source code.
Overview
In MyBatis, programmatic data queries are typically performed with just a few lines of code:
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List<LwUser> userList = userMapper.listUserByUserName("孤狼1号");The first line obtains a SqlSession (explained in a previous article). The second line retrieves the UserMapper interface, and the third line executes the entire query in a single call. The following sections analyze the second and third steps in detail.
Getting Mapper Interface (getMapper)
Calling session.getMapper triggers a runtime sequence that binds the mapper interface to its proxy:
Configuration retrieves the Mapper object because all mapper interfaces are loaded into the Configuration at startup.
MapperRegistry’s getMapper method is invoked.
Based on the type, the knownMappers map provides the corresponding proxy factory, which creates a MapperProxy instance.
The final result is a JDK dynamic proxy implementing InvocationHandler.
The MapperProxy is stored in the knownMappers map during configuration initialization.
When Mapper Interface and Mapping File Are Linked
Mapper interfaces and their XML mapping files are registered when the MyBatis configuration file is parsed. The process includes:
Calling SqlSessionFactoryBuilder.build() to create a XMLConfigBuilder.
Parsing the global configuration and then the <mappers> node.
Four possible ways to configure mappers are handled; both XML files and annotated interfaces ultimately result in a binding between the interface and its XML.
During parsing, MapperRegistry.addMapper stores the interface in knownMappers, making it retrievable via getMapper.
Annotation‑based mappings (e.g., @Select) are also parsed after the XML files.
SQL Execution Process Analysis
The execution flow consists of two major steps: locating the SQL statement and executing it.
Finding SQL
The proxy’s invoke method creates a MapperMethod that encapsulates the method’s metadata and the associated SQL. This object is then used to prepare the statement.
Executing SQL Statement
The execute method decides how to run the statement based on its return type. For collection results, executeForMany is used, which eventually calls PreparedStatement.execute and processes the result set.
Parameter Mapping
Parameter handling is delegated to StatementHandler.parameterize. MyBatis provides default TypeHandler implementations (e.g., StringTypeHandler) that call the appropriate JDBC setXxx methods. Custom TypeHandler s can be created by extending BaseTypeHandler and overriding setNonNullParameter.
public class MyTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int index, String param, JdbcType jdbcType) throws SQLException {
System.out.println("Custom typeHandler applied");
ps.setString(index, param);
}
// other methods omitted for brevity
}Custom handlers can be referenced in SQL statements:
select user_id, user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR,typeHandler=com.lonelyWolf.mybatis.typeHandler.MyTypeHandler}Result Set Mapping
Result handling also relies on TypeHandler s. After the JDBC ResultSet is obtained, ResultSetHandler.handleResultSets iterates over rows and uses the appropriate TypeHandler to map columns to object properties.
Custom TypeHandler Result Set
A full custom handler implements all four methods of BaseTypeHandler to manage setting parameters and retrieving results by column name, index, or callable statements.
public class MyTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int index, String param, JdbcType jdbcType) throws SQLException {
System.out.println("Setting parameter – custom typeHandler");
ps.setString(index, param);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("Getting result by columnName – custom typeHandler");
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
System.out.println("Getting result by columnIndex – custom typeHandler");
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}The corresponding <resultMap> can reference this handler for specific columns.
Workflow Diagram
The overall MyBatis workflow involves SqlSession delegating to four core objects: Executor, StatementHandler, ParameterHandler, and ResultSetHandler. Interceptors can target any of these components.
Summary
This article analyzed MyBatis’s SQL execution flow, covering:
How mapper interfaces bind to XML files.
The step‑by‑step SQL execution process.
Creating custom TypeHandler s for parameter and result mapping.
Understanding the high‑level workflow helps developers navigate the framework and extend it where needed.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
