How MyBatis Binds Mappers, Executes SQL, and Uses Custom TypeHandlers
This article dissects MyBatis’s internal workflow, explaining how mapper interfaces are linked to XML files, detailing the step‑by‑step SQL execution process, and demonstrating how to create and apply custom typeHandlers for parameter setting and result mapping, all based on MyBatis 3.5.5 source code.
Preface
MyBatis may be widely used, but many developers are unclear about its SQL execution flow. This article will cover:
How Mapper interfaces and mapping files are bound
MyBatis SQL execution process
Custom parameter handling with typeHandler
Custom result set handling with typeHandler
PS: The analysis is based on MyBatis 3.5.5 source code.
Overview
In MyBatis, programmatic data queries typically use 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, the second retrieves the UserMapper interface, and the third line executes the query. The following sections analyze steps two and three in detail.
Getting the Mapper Interface (getMapper)
The second step obtains a Mapper interface via the SqlSession. The runtime sequence diagram is shown below.
1. After calling getMapper, the Configuration object retrieves the Mapper because Mapper interfaces are loaded and stored in Configuration at project startup.
2. The MapperRegistry inside Configuration continues to call getMapper.
3. Based on the type, knownMappers provides the proxy factory class, which creates the Mapper proxy.
4. Finally the MapperProxy object is obtained.
MapperProxyimplements InvocationHandler using JDK dynamic proxy. The MapperRegistry stores the Mapper in its knownMappers map when Configuration.addMapper is invoked during configuration parsing.
When Are Mapper Interfaces and XML Files Associated?
Mapper interfaces and their XML files are stored when the mybatis-config.xml configuration file is parsed. The sequence diagram is as follows.
1. SqlSessionFactoryBuilder.build() is called.
2. An XMLConfigBuilder is created and its parse method is invoked.
3. The mappers node is parsed, handling four configuration styles. Two styles directly configure XML mapping files, and two configure Mapper interfaces. Regardless of the style, MyBatis ultimately links the XML file with the Mapper interface.
4. For the XML‑based styles, an XMLMapperBuilder is created and its parse method processes the mapping file.
5. After parsing, Configuration.bindMapperForNamespace binds the Mapper interface to the XML namespace, and Configuration.addMapper registers the Mapper in MapperRegistry.
At this point the Mapper interface and XML mapping file are bound.
SQL Execution Process
Because the Mapper interface is wrapped by a proxy, invoking a query method triggers the proxy’s invoke method. The execution can be divided into two major steps: locating the SQL statement and executing it.
Locating SQL
The sequence diagram shows how the proxy determines the mapped statement.
1. The proxy’s invoke method is called.
2. The method checks whether it is a default method; otherwise it creates a MapperMethod that encapsulates method and SQL information.
Executing SQL
The execution flow continues as follows.
1. Executor.execute selects the appropriate method based on the statement type and return type; for a collection result it calls executeForMany.
2. Parameters are converted and the call returns to SqlSession.selectList.
3. The call is delegated to Executor.query, which eventually invokes queryFromDatabase.
4. PreparedStatementHandler.query creates a PreparedStatement, sets parameters, and calls JDBC execute.
5. After execution, the result set is processed and returned.
Parameter Mapping
Before the query runs, parameters are set via StatementHandler.parameterize. The default BaseTypeHandler subclasses handle specific JDBC types.
For example, StringTypeHandler calls PreparedStatement.setString, while IntTypeHandler calls setInt. These handlers are automatically selected based on the Java type of the parameter.
Custom typeHandlers can be defined to override this behavior.
package com.lonelyWolf.mybatis.typeHandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
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 user_id, user_name from lw_user where user_name=#{userName,jdbcType=VARCHAR,typeHandler=com.lonelyWolf.mybatis.typeHandler.MyTypeHandler}Execution shows the custom handler being invoked.
Result Set Mapping
After the query, ResultSetHandler.handleResultSets processes the result set. The process involves converting rows to objects using the configured typeHandlers.
resultSetHandler.handleResultSets(ps)Custom result handling can be implemented by overriding the appropriate methods in a BaseTypeHandler subclass.
package com.lonelyWolf.mybatis.typeHandler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.*;
public class MyTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
System.out.println("Setting parameter -> custom typeHandler applied");
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("Getting result by columnName -> custom typeHandler applied");
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
System.out.println("Getting result by columnIndex -> custom typeHandler applied");
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}Mapper XML configuration with the custom result map:
<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>
<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>Workflow Diagram
A high‑level diagram summarizing the main objects involved in MyBatis execution:
The diagram shows SqlSession delegating to four core components, which are the focus of interceptor development covered in future articles.
Conclusion
This article analyzed MyBatis’s SQL execution flow, demonstrated how to bind Mapper interfaces to XML files, and showed how to create custom typeHandler implementations for both parameter setting and result mapping. While MyBatis’s default handlers satisfy most needs, custom handlers provide flexibility for special cases.
Understanding Mapper‑XML binding
Grasping the SQL execution pipeline
Creating custom parameter typeHandler s
Creating custom result set typeHandler s
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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
