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.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
How MyBatis Binds Mappers, Executes SQL, and Uses Custom TypeHandlers

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.

MapperProxy

implements 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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentMyBatisSQL ExecutionCustom TypeHandler
Java High-Performance Architecture
Written by

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.

0 followers
Reader feedback

How this landed with the community

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.