Java 自定义 mybatis 多拦截器协作给SQL动态批量添加过滤条件

下面将为你详细介绍如何通过自定义 MyBatis 多拦截器协作,在 XML 文件中为 SQL 添加过滤条件。我们会创建两个拦截器,一个用于在 SQL 执行前解析并添加过滤条件,另一个用于打印添加条件后的 SQL 信息。

实现思路

  1. 自定义拦截器 :创建两个实现 ​​Interceptor​​ 接口的拦截器类。第一个拦截器用于在 SQL 中添加过滤条件,第二个拦截器用于打印添加条件后的 SQL 信息。

  2. 注册拦截器 :将这些拦截器注册到 MyBatis 的 ​​InterceptorChain​​ 中。

  3. 定义拦截顺序:按照业务需求,确定各个拦截器的执行顺序。

代码示例

第一个拦截器,在 SQL 中添加过滤条件 AddFilterConditionInterceptor

java 复制代码
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 org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

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

// 第一个拦截器,在 SQL 中添加过滤条件
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class AddFilterConditionInterceptor implements Interceptor {

    private static final String FILTER_CONDITION = " AND status = 'ACTIVE'";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        BoundSql boundSql = statementHandler.getBoundSql();
        String originalSql = boundSql.getSql();

        // 检查 SQL 是否为 SELECT 语句
        if (originalSql.trim().toUpperCase().startsWith("SELECT")) {
            String newSql = originalSql + FILTER_CONDITION;
            metaObject.setValue("delegate.boundSql.sql", newSql);
        }

        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 设置属性
    }
}

第二个拦截器,打印添加条件后的 SQL 信息 PrintSqlInterceptor

java 复制代码
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;

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

// 第二个拦截器,打印添加条件后的 SQL 信息
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PrintSqlInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        System.out.println("Modified SQL: " + sql);
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 设置属性
    }
}

MyBatis 配置类,注册拦截器

java 复制代码
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

// MyBatis 配置类,注册拦截器
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);

        // 创建拦截器实例
        AddFilterConditionInterceptor addFilterConditionInterceptor = new AddFilterConditionInterceptor();
        PrintSqlInterceptor printSqlInterceptor = new PrintSqlInterceptor();

        // 获取 MyBatis 配置
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        // 添加拦截器
        configuration.addInterceptor(addFilterConditionInterceptor);
        configuration.addInterceptor(printSqlInterceptor);

        sessionFactory.setConfiguration(configuration);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
        return sessionFactory.getObject();
    }
}

代码解释

  1. ​AddFilterConditionInterceptor​
  • 该拦截器在 SQL 执行前进行拦截。
  • 通过 ​MetaObject​ 获取原始 SQL 语句。
  • 检查 SQL 是否为 ​SELECT​ 语句,如果是,则在原 SQL 后面添加过滤条件 ​AND status = 'ACTIVE'​
  • 使用 ​metaObject.setValue​ 方法更新 SQL 语句。
  1. ​PrintSqlInterceptor​
  • 该拦截器在 SQL 执行前进行拦截。
  • 获取添加过滤条件后的 SQL 语句并打印。
  1. ​MyBatisConfig​
  • 这是 MyBatis 的配置类,通过 ​​SqlSessionFactoryBean​​​ 创建 ​​SqlSessionFactory​​。

  • 在配置中添加自定义的拦截器,拦截器的添加顺序即为执行顺序。

注意事项

  • 拦截器的执行顺序由添加到 ​Configuration​ 中的顺序决定。
  • 过滤条件的添加逻辑可以根据实际需求进行修改,例如从上下文或参数中获取动态条件。
  • 多个拦截器之间要避免逻辑冲突,以免影响系统的正常运行。
  • 拦截器的逻辑要尽量简洁,避免影响 SQL 的执行性能。
相关推荐
架构师沉默7 分钟前
Java 开发者别忽略 return!这 11 种写法你写对了吗?
java·后端·架构
EndingCoder9 分钟前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js
RainbowJie115 分钟前
Gemini CLI 与 MCP 服务器:释放本地工具的强大潜力
java·服务器·spring boot·后端·python·单元测试·maven
ITMan彪叔24 分钟前
Nodejs打包 Webpack 中 __dirname 的正确配置与行为解析
javascript·后端
用户895356032822041 分钟前
告别重复,用Go泛型精简Gin代码
后端·gin
运维开发故事1 小时前
AIOps系列 | 开发一个 K8s Chat 命令行工具
后端
惜鸟1 小时前
大模型工具/函数调用原理和实践
后端
神毓逍遥kang1 小时前
nestjs drizzle-orm 构建rbac权限系统
前端·后端
用户298698530141 小时前
如何使用 Spire.Doc 将 Word 转换为 TIFF?
后端
颜如玉1 小时前
位运算技巧总结
后端·算法·性能优化