MyBatis 插件开发的完整详细例子

MyBatis 插件开发的完整详细例子

MyBatis 插件(Interceptor)允许开发者在已映射语句执行过程中的某一点进行拦截调用,从而实现自定义逻辑。以下是一个完整的 MyBatis 插件开发示例,涵盖所有使用场景,并附有详细注释和总结。


1. MyBatis 插件基础

MyBatis 允许拦截以下接口的方法:

  • Executorupdate, query, flushStatements, commit, rollback, getTransaction, close, isClosed
  • ParameterHandlergetParameterObject, setParameters
  • ResultSetHandlerhandleResultSets, handleCursorResultSets, handleOutputParameters
  • StatementHandlerprepare, parameterize, batch, update, query

2. 插件开发示例

2.1. 自定义插件类

创建一个自定义插件类 MyPlugin,该插件将拦截 Executorquery 方法和 StatementHandlerprepare 方法。

java 复制代码
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.statement.StatementHandler;

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

@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取被拦截方法的参数
        Object[] args = invocation.getArgs();

        // 拦截 Executor.query 方法
        if (invocation.getTarget() instanceof Executor) {
            MappedStatement ms = (MappedStatement) args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds) args[2];
            ResultHandler resultHandler = (ResultHandler) args[3];

            BoundSql boundSql = ms.getBoundSql(parameter);
            String sql = boundSql.getSql();
            System.out.println("Executing SQL: " + sql);

            // 执行原方法
            return invocation.proceed();
        }

        // 拦截 StatementHandler.prepare 方法
        if (invocation.getTarget() instanceof StatementHandler) {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            Connection connection = (Connection) args[0];
            Integer integer = (Integer) args[1];

            // 获取原始 SQL
            BoundSql boundSql = statementHandler.getBoundSql();
            String originalSql = boundSql.getSql();
            System.out.println("Original SQL: " + originalSql);

            // 修改 SQL(例如添加注释)
            String newSql = "/* MyPlugin */ " + originalSql;
            BoundSql newBoundSql = new BoundSql(
                boundSql.getMappedStatement().getConfiguration(),
                newSql,
                boundSql.getParameterMappings(),
                boundSql.getParameterObject()
            );
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
            metaObject.setValue("delegate.boundSql", newBoundSql);

            // 执行原方法
            return invocation.proceed();
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 使用 Plugin.wrap 包装目标对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性(可选)
    }
}
2.2. 配置插件

mybatis-config.xml 中配置自定义插件:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置插件 -->
    <plugins>
        <plugin interceptor="com.example.plugin.MyPlugin">
            <!-- 可以设置插件属性 -->
            <!-- <property name="propertyName" value="propertyValue"/> -->
        </plugin>
    </plugins>

    <!-- 其他配置... -->
</configuration>
2.3. 测试插件

编写测试代码来验证插件的功能:

java 复制代码
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.Reader;

public class PluginTest {
    public static void main(String[] args) throws Exception {
        // 读取 MyBatis 配置文件
        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            UserMapper mapper = session.getMapper(UserMapper.class);

            // 执行查询操作,触发插件拦截
            User user = mapper.selectUserById(1L);
            System.out.println("User: " + user.getName());
        }
    }
}

3. 核心使用场景

3.1. 日志记录

Executorqueryupdate 方法中添加日志记录,便于调试和监控。

3.2. SQL 修改

StatementHandlerprepare 方法中修改 SQL 语句,例如添加统一的注释或进行性能优化。

3.3. 参数处理

ParameterHandlersetParameters 方法中对参数进行预处理,如加密、格式化等。

3.4. 结果集处理

ResultSetHandlerhandleResultSets 方法中对结果集进行后处理,如数据脱敏、缓存等。


4. 表格整理总结

场景 拦截接口及方法 具体应用 示例代码片段
日志记录 Executor.query, Executor.update 在执行 SQL 前后记录日志 System.out.println("Executing SQL: " + sql);
SQL 修改 StatementHandler.prepare 修改或优化 SQL 语句 String newSql = "/* MyPlugin */ " + originalSql;
参数处理 ParameterHandler.setParameters 对参数进行预处理(如加密) preparedStatement.setString(1, encrypt(parameter));
结果集处理 ResultSetHandler.handleResultSets 对查询结果进行后处理(如脱敏) resultList.forEach(item -> item.setEmail(maskEmail(item.getEmail())));
性能监控 Executor.query, Executor.update 记录 SQL 执行时间 long startTime = System.currentTimeMillis(); ... long endTime = ...
分页支持 StatementHandler.parameterize 动态添加分页参数 preparedStatement.setInt(1, offset); preparedStatement.setInt(2, limit);

5. 最佳实践建议

  1. 理解底层行为:在重写方法时,需理解其底层行为,避免破坏 MyBatis 的核心功能。
  2. 谨慎修改 SQL:修改 SQL 时要确保语法正确,避免引入潜在的 SQL 注入风险。
  3. 合理使用属性 :通过 setProperties 方法为插件设置属性,增强灵活性。
  4. 单元测试:编写单元测试验证插件功能,确保其在各种场景下的稳定性。
  5. 文档记录:详细记录插件的使用方法和注意事项,便于团队成员理解和维护。

通过以上示例和总结,可以全面掌握 MyBatis 插件的开发和应用场景。

相关推荐
空空kkk14 小时前
MyBatis——代理Dao方式的增删改查操作
java·数据库·mybatis
2501_941800881 天前
Java高性能搜索引擎与Lucene实战分享:大规模文本索引、检索与优化经验
mybatis
q***42822 天前
SpringCloud-持久层框架MyBatis Plus的使用与原理详解
spring·spring cloud·mybatis
北郭guo2 天前
MyBatis框架讲解,工作原理、核心内容、如何实现【从浅入深】让你看完这篇文档对于MyBatis的理解更加深入
java·数据库·mybatis
♡喜欢做梦3 天前
MyBatis XML 配置文件:从配置规范到 CRUD 开发实践
xml·java·java-ee·mybatis
q***69773 天前
Spring Boot与MyBatis
spring boot·后端·mybatis
tanxiaomi3 天前
Spring、Spring MVC 和 Spring Boot ,mybatis 相关面试题
java·开发语言·mybatis
q***96583 天前
Spring Boot 集成 MyBatis 全面讲解
spring boot·后端·mybatis
p***93034 天前
Spring Boot中集成MyBatis操作数据库详细教程
数据库·spring boot·mybatis
chxii4 天前
在 MyBatis 中开启 SQL 日志
java·数据库·mybatis