Backend Development 17 min read

Deep Dive into MyBatis SQL Execution Process and Custom TypeHandler Implementation

This article explains MyBatis’s internal workflow, covering how mapper interfaces bind to XML files, the step‑by‑step SQL execution pipeline, parameter handling, result set mapping, and how to create custom TypeHandler classes for both input parameters and query results.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Deep Dive into MyBatis SQL Execution Process and Custom TypeHandler Implementation

Introduction

MyBatis is widely used, but many developers are not familiar with its internal SQL execution flow. This article will help you understand how mapper interfaces are bound to XML files, how SQL statements are processed, and how to create custom TypeHandler implementations.

Overview

Typical programmatic query in MyBatis consists of the following code:

SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
List
userList = userMapper.listUserByUserName("孤狼1号");

The first line obtains a SqlSession , the second retrieves the UserMapper interface, and the third line executes the whole query with a single method call.

Getting the Mapper Interface (getMapper)

Calling session.getMapper triggers a series of steps:

Configuration retrieves the mapper definition that was loaded during MyBatis startup.

MapperRegistry looks up the mapper class and creates a proxy factory.

The proxy factory generates a JDK dynamic proxy ( MapperProxy ) that implements the mapper interface.

The proxy is stored in knownMappers for later retrieval.

The proxy implements InvocationHandler , which means method calls are delegated to MyBatis’s internal execution logic.

Binding Mapper Interfaces to XML Files

When the mybatis-config.xml is parsed, MyBatis loads all mapper interfaces and their corresponding XML files. The process includes:

Calling SqlSessionFactoryBuilder.build() to create a XMLConfigBuilder .

Parsing the configuration file and locating the <mappers> node.

For each mapper entry, either an XML file or a mapper interface is processed, ultimately linking the interface to its XML statements.

The Configuration.addMapper method stores the mapper in knownMappers .

SQL Execution Process

The execution consists of two major phases: locating the SQL statement and executing it.

Locating SQL

The proxy’s invoke method creates a MapperMethod object that encapsulates method metadata and the associated SQL. It then retrieves the mapped statement from the configuration.

Executing SQL

Depending on the return type, MyBatis calls executeForMany (for collections) or other execution methods. The flow includes:

Parameter conversion via StatementHandler.parameterize .

Creating a PreparedStatement and setting JDBC parameters.

Delegating to PreparedStatementHandler.query , which finally invokes PreparedStatement.execute .

Result handling through ResultSetHandler.handleResultSets , which uses the appropriate TypeHandler to map rows to objects.

Parameter Mapping

MyBatis uses BaseTypeHandler subclasses to set JDBC parameters. For example, StringTypeHandler calls PreparedStatement.setString . Custom TypeHandler s can be defined to control this process.

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 applied for setting parameter");
        ps.setString(i, parameter);
    }
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        System.out.println("Custom TypeHandler applied for retrieving by column name");
        return rs.getString(columnName);
    }
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        System.out.println("Custom TypeHandler applied for retrieving by column index");
        return rs.getString(columnIndex);
    }
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

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>

Result Set Mapping

After the query executes, ResultSetHandler.handleResultSets processes the rows. The handler iterates over the result set, uses the configured TypeHandler to read column values, and populates the result objects.

Workflow Diagram

The diagram below summarizes the main objects involved in MyBatis’s execution flow (SqlSession, Executor, StatementHandler, ParameterHandler, ResultSetHandler).

Conclusion

This article dissected MyBatis’s SQL execution pipeline, demonstrated how mapper interfaces bind to XML files, and showed how to implement custom TypeHandler s for both parameter setting and result mapping. Understanding these mechanisms helps you extend MyBatis when default behavior is insufficient.

JavaMyBatisORMSQL ExecutionTypeHandler
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.