插件窝 干货文章 MyBatis 批量插入数据时,拦截器为什么失效,如何解决?

MyBatis 批量插入数据时,拦截器为什么失效,如何解决?

拦截器 批量 Invocation MappedStatement 691    来源:    2025-03-31

MyBatis 批量插入拦截器失效问题分析与解决方案

问题原因分析

MyBatis 批量插入时拦截器失效通常由以下几个原因导致:

  1. Executor 类型不同:批量操作通常使用 BatchExecutor,而普通操作用 SimpleExecutorReuseExecutor,拦截器可能没有正确处理不同 Executor 类型

  2. SQL 语句生成方式不同:批量插入可能使用 <foreach> 标签生成动态 SQL,与普通插入语句结构不同

  3. 拦截点选择不当:拦截器可能没有覆盖批量操作的关键拦截点

  4. 插件配置顺序问题:多个插件间的执行顺序可能影响批量操作

解决方案

1. 确保拦截器覆盖批量操作

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "flushStatements", args = {})
})
public class BatchInterceptor implements Interceptor {
    // 实现逻辑
}

2. 检查 Executor 类型

在拦截器中处理 BatchExecutor 的情况:

@Override
public Object intercept(Invocation invocation) throws Throwable {
    if (invocation.getTarget() instanceof BatchExecutor) {
        // 处理批量操作逻辑
    }
    return invocation.proceed();
}

3. 处理动态 SQL

对于 <foreach> 生成的批量插入语句,需要检查 MappedStatement 的 SQL 命令类型:

MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
if (ms.getSqlCommandType() == SqlCommandType.INSERT) {
    // 处理插入逻辑
}

4. 配置正确的插件顺序

确保拦截器在 mybatis-config.xml 中正确配置:

<plugins>
    <plugin interceptor="com.example.BatchInterceptor">
        <!-- 如果有参数 -->
    </plugin>
</plugins>

5. 完整示例代码

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "flushStatements", args = {})
})
public class BatchInsertInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];

        if (ms.getSqlCommandType() == SqlCommandType.INSERT 
            && invocation.getTarget() instanceof BatchExecutor) {
            // 处理批量插入逻辑
            System.out.println("拦截到批量插入操作");
            // 可以在这里修改参数或执行其他逻辑
        }

        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 可以获取配置的属性
    }
}

其他注意事项

  1. 批量提交大小:检查是否因为批量提交大小设置导致拦截器看似"失效"
  2. 事务管理:确保在事务提交前拦截器有机会执行
  3. 日志调试:添加详细日志以确定拦截器是否被调用及调用时机
  4. MyBatis 版本:不同版本可能有不同的拦截器行为,检查版本兼容性

通过以上方法,应该能够解决 MyBatis 批量插入时拦截器失效的问题。