Backend Development 21 min read

Deep Dive into MyBatis Core Components: Executor, StatementHandler, ParameterHandler, and ResultSetHandler

This article provides a comprehensive, English-language walkthrough of MyBatis's four core components—Executor, StatementHandler, ParameterHandler, and ResultSetHandler—explaining their interfaces, default implementations, key methods, and how they collaborate to translate Java objects into SQL statements and process query results.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Deep Dive into MyBatis Core Components: Executor, StatementHandler, ParameterHandler, and ResultSetHandler

Introduction

In the previous article we introduced MyBatis plugins and mentioned that they can intercept the four major MyBatis components, but we only showed a high‑level diagram. This article fills that gap by detailing the purpose, interfaces, and default implementations of those components.

1. Purpose of the Four Core Components

Below is a class‑reference diagram of MyBatis (image omitted for brevity). The four components are Executor, StatementHandler, ParameterHandler, and ResultSetHandler.

2. Executor

Executor Interface Methods

The Executor interface defines the following methods:

public interface Executor {
    ResultHandler NO_RESULT_HANDLER = null;
    int update(MappedStatement ms, Object parameter) throws SQLException;
List
query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
        CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List
query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
        throws SQLException;
Cursor
queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
    List
flushStatements() throws SQLException;
    void commit(boolean required) throws SQLException;
    void rollback(boolean required) throws SQLException;
    CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
    boolean isCached(MappedStatement ms, CacheKey key);
    void clearLocalCache();
    void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class
targetType);
    Transaction getTransaction();
    void close(boolean forceRollback);
    boolean isClosed();
    void setExecutorWrapper(Executor executor);
}

The executor lives inside a SqlSession and mainly handles three responsibilities: data query/update, transaction commit/rollback, and MyBatis cache management.

Function Description

The default implementations are:

CachingExecutor – a second‑level cache container that delegates actual SQL execution to another executor.

BaseExecutor – an abstract class that provides transaction handling and first‑level cache preparation; core query/update logic is left to subclasses.

SimpleExecutor – creates a new Statement for each SQL and closes it immediately; suitable for short‑lived operations.

ReuseExecutor – reuses existing Statement objects for the same SQL to improve performance.

BatchExecutor – batches multiple SQL statements into a single Statement to reduce round‑trips.

Example of an executor method:

public
List
doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
        ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter,
                rowBounds, resultHandler, boundSql);
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    } finally {
        closeStatement(stmt);
    }
}

The executor creates a StatementHandler , uses it to build a Statement , executes the SQL, and finally hands the result to a ResultSetHandler .

3. StatementHandler

Statement Introduction

In JDBC, java.sql.Statement is the core object used to execute static SQL statements and obtain results.

StatementHandler Interface Methods

public interface StatementHandler {
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
    void parameterize(Statement statement) throws SQLException;
    void batch(Statement statement) throws SQLException;
    int update(Statement statement) throws SQLException;
List
query(Statement statement, ResultHandler resultHandler) throws SQLException;
Cursor
queryCursor(Statement statement) throws SQLException;
    BoundSql getBoundSql();
    ParameterHandler getParameterHandler();
}

Its main responsibilities are creating a Statement , binding parameters, executing the SQL, and returning the result set.

Implementation Classes

RoutingStatementHandler – routes to a concrete handler based on the statement type.

BaseStatementHandler – abstract base that stores common fields such as ParameterHandler and ResultHandler .

PreparedStatementHandler – handles prepared (parameterized) SQL.

CallableStatementHandler – handles stored procedures.

SimpleStatementHandler – handles simple static SQL without parameters.

Typical usage with a prepared statement:

@Override
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
}

public
List
query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
}

4. ParameterHandler

Interface Methods

public interface ParameterHandler {
    Object getParameterObject();
    void setParameters(PreparedStatement ps) throws SQLException;
}

The default implementation is DefaultParameterHandler . Its constructor receives a MappedStatement , the user‑provided parameter object, and the BoundSql containing the SQL with placeholders.

public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
}

The setParameters method iterates over ParameterMapping entries, resolves each value (including additional parameters, null handling, and type conversion), obtains the appropriate TypeHandler , and finally calls typeHandler.setParameter to bind the value to the PreparedStatement .

public void setParameters(PreparedStatement ps) {
    List
parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping pm = parameterMappings.get(i);
            if (pm.getMode() != ParameterMode.OUT) {
                Object value;
                String property = pm.getProperty();
                if (boundSql.hasAdditionalParameter(property)) {
                    value = boundSql.getAdditionalParameter(property);
                } else if (parameterObject == null) {
                    value = null;
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    value = parameterObject;
                } else {
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    value = metaObject.getValue(property);
                }
                TypeHandler typeHandler = pm.getTypeHandler();
                JdbcType jdbcType = pm.getJdbcType();
                if (value == null && jdbcType == null) {
                    jdbcType = configuration.getJdbcTypeForNull();
                }
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
            }
        }
    }
}

TypeHandler

public interface TypeHandler
{
    void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
    T getResult(ResultSet rs, String columnName) throws SQLException;
    T getResult(ResultSet rs, int columnIndex) throws SQLException;
    T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

MyBatis ships with dozens of built‑in TypeHandler implementations (e.g., for String , Integer , Date , etc.) and allows custom handlers.

5. ResultSetHandler

Interface Methods

public interface ResultSetHandler {
List
handleResultSets(Statement stmt) throws SQLException;
Cursor
handleCursorResultSets(Statement stmt) throws SQLException;
    void handleOutputParameters(CallableStatement cs) throws SQLException;
}

The default implementation ( DefaultResultSetHandler ) iterates over the ResultSet , creates result objects based on the mapped ResultMap , and stores them via a ResultHandler .

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap,
        ResultHandler
resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    DefaultResultContext
resultContext = new DefaultResultContext<>();
    ResultSet rs = rsw.getResultSet();
    skipRows(rs, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !rs.isClosed() && rs.next()) {
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);
        Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
        storeObject(resultHandler, resultContext, rowValue, parentMapping, rs);
    }
}

The ResultHandler interface is a simple callback used to collect or process each row; MyBatis provides DefaultResultHandler (stores rows in a List ) and MapResultHandler (stores rows in a Map ).

Conclusion

ParameterHandler translates Java objects into SQL parameters, ResultSetHandler converts the raw JDBC ResultSet into Java objects, StatementHandler orchestrates the creation of Statement , parameter binding, and execution, while Executor acts as the high‑level manager that coordinates these components, handles transactions, and manages caching.

JavaMyBatisORMExecutorParameterHandlerResultSetHandlerStatementHandler
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

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.