Deep Dive into MyBatis SQL Execution Process and Custom TypeHandler
This article explains how MyBatis binds mapper interfaces to XML files, details the complete SQL execution flow—including mapper retrieval, statement preparation, parameter handling, and result mapping—and demonstrates how to create custom TypeHandlers for both parameter setting and result set processing.
Welcome, I am Lei Ge.
Overview
This article analyzes the MyBatis SQL execution process based on MyBatis 3.5.5 source code, covering mapper binding, SQL execution steps, parameter mapping, result set mapping, and custom TypeHandler implementation.
Mapper Retrieval (getMapper)
When session.getMapper(UserMapper.class) is called, MyBatis looks up the Mapper object in the Configuration 's MapperRegistry . The registry holds a map of known mappers that were loaded during configuration parsing. The retrieved mapper is a JDK dynamic proxy ( MapperProxy ) implementing InvocationHandler .
Binding Mapper Interfaces to XML Files
During SqlSessionFactoryBuilder.build() , MyBatis parses mybatis-config.xml . The XMLConfigBuilder processes the <mappers> node, loading each mapper XML file or mapper interface. Both approaches ultimately associate the XML statements with the mapper interface via the Configuration object.
SQL Execution Process
The execution consists of two major steps: locating the SQL statement and executing it. The proxy’s invoke method creates a MapperMethod that holds method and SQL metadata, then delegates to Executor to run the query.
For a collection result, Executor.executeForMany is invoked, which eventually calls PreparedStatementHandler.query to execute the JDBC PreparedStatement and then processes the ResultSet .
Parameter Mapping
Before execution, MyBatis calls StatementHandler.parameterize to set parameters. Default BaseTypeHandler subclasses (e.g., StringTypeHandler ) call the appropriate JDBC setXXX method. Custom TypeHandlers can be defined to override this behavior.
select user_id,user_name from lw_user where user_name=#{userName}Adding jdbcType or a custom typeHandler makes the mapping explicit.
Custom TypeHandler Example
A custom TypeHandler must extend BaseTypeHandler<String> and implement four methods for setting parameters and retrieving results.
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
{
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
System.out.println("Custom TypeHandler set parameter");
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
System.out.println("Custom TypeHandler get result by column name");
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
System.out.println("Custom TypeHandler get result by column index");
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
}Using the handler in a mapper XML:
<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>Running the query prints the custom handler messages, confirming it is active.
Result Set Mapping
After the JDBC query, ResultSetHandler.handleResultSets processes the rows. It uses the configured typeHandler to convert column values to Java objects, supporting both column name and index retrieval.
Workflow Diagram
The following diagram visualizes the main objects involved in MyBatis execution (SqlSession, Configuration, Executor, MapperProxy, etc.).
Conclusion
The article demonstrates how MyBatis binds mapper interfaces to XML, the complete SQL execution flow, and how to customize parameter and result mapping via TypeHandlers. Understanding these core mechanisms helps developers extend MyBatis for special use‑cases while relying on its default behavior for most scenarios.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.