Mastering MyBatis Plugins: Features, Mechanics, Real‑World Use Cases & Best Practices

This article explains MyBatis plugins’ core capabilities—SQL rewriting, logging, performance monitoring, transaction enhancement, and result handling—detailing their interception points, the Java dynamic‑proxy implementation, a complete pagination plugin example, typical application scenarios, and practical best‑practice recommendations for developers.

Programmer1970
Programmer1970
Programmer1970
Mastering MyBatis Plugins: Features, Mechanics, Real‑World Use Cases & Best Practices

Features of MyBatis Plugins

MyBatis plugins extend the ORM framework by intercepting and modifying the execution of SQL statements. The main capabilities include:

SQL rewriting : modify SQL before it is sent to the database.

Logging : record SQL, parameters, execution time, etc., for troubleshooting and performance analysis.

Performance monitoring : collect execution time, call count and other metrics.

Transaction management enhancement : add custom logic before commit or rollback.

Result‑set handling : post‑process query results, e.g., formatting or data masking.

The plugins can intercept the four core MyBatis components:

Executor : executes SQL and manages transactions.

StatementHandler : prepares and sets parameters for SQL.

ParameterHandler : converts user parameters to JDBC‑compatible values.

ResultSetHandler : transforms JDBC result sets into objects or collections.

How MyBatis Plugins Work

The mechanism relies on Java’s dynamic‑proxy feature. When MyBatis starts, it detects configured plugins and creates proxy objects for the target components. The interception flow is:

The proxy checks whether a plugin is defined for the invoked method.

If a matching interceptor exists, MyBatis calls the interceptor’s intercept method.

Inside intercept, developers can modify arguments, replace the original call, or add extra behavior.

After the custom logic, the interceptor may proceed with the original method via reflection or return a custom result.

Because interception is based on method signatures, developers should choose intercept points carefully to avoid unnecessary performance overhead.

Pagination Plugin Implementation

The following example shows a pagination plugin that rewrites the original SQL to add a LIMIT clause, then disables MyBatis’s in‑memory pagination.

package com.example.mybatis.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PaginationPlugin implements Interceptor {

    private static final String DIALECT_MYSQL = "mysql";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);

        // Unwrap multiple proxies to get the original target
        while (metaStatementHandler.hasGetter("h")) {
            Object object = metaStatementHandler.getValue("h");
            metaStatementHandler = SystemMetaObject.forObject(object);
        }

        // Get the mapped statement
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");

        // Intercept only queries that end with "ByPage"
        if (mappedStatement.getId().endsWith("ByPage")) {
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            // Obtain pagination parameters
            PaginationParam paginationParam = (PaginationParam) boundSql.getParameterObject();
            String pageSql = buildPageSql(sql, paginationParam);
            // Replace the original SQL with the paginated version
            Field sqlField = boundSql.getClass().getDeclaredField("sql");
            sqlField.setAccessible(true);
            sqlField.set(boundSql, pageSql);

            // Disable MyBatis in‑memory pagination
            metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.DEFAULT.getOffset());
            metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.DEFAULT.getLimit());
        }

        // Continue with the original method
        return invocation.proceed();
    }

    private String buildPageSql(String sql, PaginationParam paginationParam) {
        // Simple pagination example
        String pageSql = sql + " LIMIT " + paginationParam.getOffset() + "," + paginationParam.getLimit();
        return pageSql;
    }

    @Override
    public Object plugin(Object target) {
        // Create proxy object
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // Process plugin properties if needed
    }

    // Pagination parameter class
    public static class PaginationParam {
        private int offset; // start row
        private int limit;  // rows per page
        // getters and setters omitted
    }
}

Register the plugin in mybatis-config.xml:

<configuration>
    <!-- other configurations -->
    <plugins>
        <plugin interceptor="com.example.mybatis.plugin.PaginationPlugin">
            <!-- optional plugin properties go here -->
        </plugin>
    </plugins>
    <!-- other configurations -->
</configuration>

Typical Application Scenarios

Logging & performance monitoring : capture detailed execution logs and metrics.

SQL rewriting & optimization : dynamically modify queries, add pagination, or apply business‑specific conditions.

Data masking & formatting : mask sensitive fields (e.g., phone numbers) before returning results.

Transaction management enhancement : inject custom logic around commit/rollback for auditing or additional checks.

Multi‑datasource routing & sharding : switch databases or tables based on request context.

Permission control & audit : enforce access rules and record operation history.

Best‑Practice Recommendations

Select interception points carefully : choose methods with low call frequency and minimal impact on business logic to reduce overhead.

Keep plugins independent : design them as reusable components without coupling to specific business code.

Thoroughly test and validate : especially when modifying SQL, ensure data integrity and consistency across edge cases.

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.

JavaPluginBest PracticesMyBatisPaginationDynamic ProxySQL Interception
Programmer1970
Written by

Programmer1970

Formerly called 'Code to 35'. Add our main WeChat ID to access a wealth of shared resources (algorithms, interview prep, tech stacks: Java, Python, Go, big data). We mainly share serious development techniques, focusing on output-driven input. Occasionally we post life snippets and gossip. Our aim is to attract precise traffic and test advertising opportunities.

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.