springboot通过mybatis拦截器给sql增加额外条件

1.导入jar包(如果用的若依框架则不用,本身就存在该包)

<!--sql解析类-->
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>1.1</version>
</dependency>

2.mybatis-config.xml关键如下(完整的参考ruoyi-vue)

   <plugins>
        <plugin interceptor="com.ruoyi.system.in.SqlWhereInterceptor" />
    </plugins>

3.注解如下

import java.lang.annotation.*;

@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface KxsMybatis
{
    /**
     * 可以用这个条件,自己实现,注解哦都有了,能拿到这个where去拼接sql
     */
    public String where() default "";
}

4.mapper接口如下

import com.ruoyi.common.annotation.KxsMybatis;
import com.ruoyi.common.core.domain.entity.SysUser;
import java.util.List;
/**
 * 用户表 数据层
 * 
 * @author ruoyi
 */
public interface SysUserMapper {
    @KxsMybatis
    List<SysUser> selectUserList(SysUser sysUser);
}

2.我这是在ruoyi-vue上修改的


import com.ruoyi.common.annotation.KxsMybatis;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;

/**
 * @author kxs
 * @date 2024-06-05 9:20
 */
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlWhereInterceptor implements Interceptor {
    private static Logger log = LoggerFactory.getLogger(SqlWhereInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler sh = (StatementHandler)invocation.getTarget();
        Object params = sh.getBoundSql().getParameterObject();
        String sql = sh.getBoundSql().getSql();
        log.info("\nsql入参 ===========> " + params);
        log.info("\nsql语句 ===========> " + sql);
        String mapperMethod = getMapperMethod(sh);
        log.info("\nmapperMethod ===========> " + mapperMethod);
       //获取mapper 类
        Class<?> classType = Class.forName(mapperMethod.substring(0, mapperMethod.lastIndexOf(".")));
        //方法名字
        boolean isAnnotation = false;
        String mName = mapperMethod.substring(mapperMethod.lastIndexOf(".") + 1, mapperMethod.length());
        for (Method method : classType.getDeclaredMethods()) {
            if (mName.equals(method.getName()) && method.isAnnotationPresent(KxsMybatis.class)) {
                isAnnotation = true;
                log.info("\n" + mName+"======================存在注解==================================");
            }
        }
        if(!isAnnotation){
            //注解不存在 直接正常执行
            return invocation.proceed();
        }

        CCJSqlParserManager parserManager = new CCJSqlParserManager();
        Statement parse = parserManager.parse(new StringReader(sql));
        if(parse instanceof Select) {
            //是查询的情况下
            Select select = (Select) parse;
            PlainSelect plain = (PlainSelect) select.getSelectBody();
            Expression expWhere = plain.getWhere();
            String  where = expWhere != null ? expWhere.toString() : null;
            log.info("\nwhere条件=================" + where);
            //不要直接where+"1=1"因为where如果是null会变成null 1=1
            where = StringUtils.isEmpty(where) ? "1=1" : where+" and 1=1 ";
            /**
             * MetaObject paramsObject = SystemMetaObject.forObject(params);
             * 可以用这个代码拿到入参某个值
             *例如你入参是个 User对象 User里面有ids,那么你可以通过这个拿到ids
             * Object ids = paramsObject.getValue("ids");
             */
            log.info("\nnewwhere条件=================" + where);
            Expression whereExpression = CCJSqlParserUtil
                    .parseCondExpression(where);
            plain.setWhere(whereExpression);
            log.info("\nwhere条件=================" + plain.getWhere());
            //如果自己能处理sql的话,newSql就是你处理后的,如果自己麻烦,建议用上面的,帮你处理过的,可以直接拿到where重新set
            String newSql = select.toString();
            Field sf = ReflectionUtils.findField(sh.getBoundSql().getClass(), "sql");
            sf.setAccessible(true);
            ReflectionUtils.setField(sf, sh.getBoundSql(), newSql);

        }
        // 继续执行后续操作
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        // 可以设置一些属性,但在这个示例中我们不需要
    }

    private String getMapperMethod(StatementHandler statementHandler) {
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        // 提取 Mapper 方法名,这通常涉及到解析 SQL 语句的 id 或者其他元数据
        // 注意:以下代码只是示例,实际实现可能依赖于 MyBatis 的内部实现和版本
        String mapperId = (String) metaStatementHandler.getValue("delegate.mappedStatement.id");
        // 这里你可能需要进一步解析 mapperId 来获取方法名
        // 或者使用其他方式来获取方法名,比如从参数对象中解析
        // 此处代码需要根据实际情况进行编写
        return mapperId; // 在这个例子中,我们简单地返回了 mapperId,实际中需要处理以得到方法名
    }

}
<think>嗯,用户想了解如何在Spring Boot中配置MyBatisSQL拦截器。首先,我需要回忆一下MyBatis拦截器机制。MyBatis拦截器可以用于在SQL执行前后进行一些处理,比如日志记录、性能监控、分页处理等。用户可能是想实现类似的功能。 接下来,我应该考虑如何在Spring Boot项目中配置这个拦截器。首先,用户需要创建一个实现MyBatis的Interceptor接口的类,并重写intercept方法。这里可能需要举一个例子,比如记录SQL执行时间,这样用户更容易理解。 然后,需要使用@Intercepts注解来指定拦截的方法,比如Executor类的update和query方法。同时,在配置类中,通过@Bean方法将拦截器注册到SqlSessionFactory中。这里需要注意,如果用户已经使用了MyBatis的starter,可能需要通过BeanPostProcessor来处理,或者在配置类中手动添加拦截器。 另外,要提醒用户注意拦截器的顺序,如果有多个拦截器的话,执行顺序可能会影响结果。还要提到可能的常见应用场景,比如分页、敏感数据加密、SQL日志等,这样用户能更好地了解拦截器的用途。 最后,检查是否有遗漏的步骤,比如Maven依赖是否正确,是否需要额外的配置。确保示例代码的正确性,特别是@Configuration类的配置部分,以及拦截器本身的实现。可能还需要提到使用@Signature注解来指定拦截的具体方法参数,避免用户在这里出错。</think>在Spring Boot中配置MyBatis SQL拦截器可以通过以下步骤实现(以记录SQL执行时间的拦截器为例): --- ### 一、创建拦截器类 ```java @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class SqlCostInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); // 执行原方法 } finally { long cost = System.currentTimeMillis() - start; String sqlId = ((MappedStatement) invocation.getArgs()[0]).getId(); System.out.println("SQL [" + sqlId + "] 执行耗时:" + cost + "ms"); } } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 可读取配置文件中的参数 } } ``` --- ### 二、注册拦截器 在配置类中将拦截器注册到MyBatis: ```java @Configuration public class MyBatisConfig { @Bean public SqlCostInterceptor sqlCostInterceptor() { return new SqlCostInterceptor(); } @Bean public ConfigurationCustomizer mybatisConfigurationCustomizer() { return configuration -> { configuration.addInterceptor(sqlCostInterceptor()); }; } } ``` --- ### 三、添加必要依赖 确保`pom.xml`包含: ```xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> ``` --- ### 四、拦截器常见用途 1. **SQL性能监控**:记录执行时间 2. **分页处理**:自动改写分页SQL 3. **敏感数据加密**:加解密特定字段 4. **SQL日志记录**:输出完整可执行的SQL 5. **租户隔离**:自动添加tenant_id条件 --- ### 注意事项 - 多个拦截器按注册顺序执行 - 谨慎处理`invocation.proceed()`的返回值 - 避免在拦截器中执行耗时操作 - 通过`@Signature`精确指定要拦截的方法 可以通过`@Intercepts`注解组合多个拦截点,实现更复杂的SQL监控需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值