MyBatis拦截器

拦截器的作用

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护。
ParameterHandler (getParameterObject, setParameters),负责对用户传递的参数转换成JDBC Statement 所需要的参数。
ResultSetHandler (handleResultSets, handleOutputParameters),负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
StatementHandler (prepare, parameterize, batch, update, query),封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为,
因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

实际的应用:在很多业务场景下我们需要去拦截sql,达到不入侵原有代码业务处理一些东西,比如:分页操作,数据权限过滤操作,SQL执行时间性能监控等等,这里我们就可以用到Mybatis的拦截器Interceptor
原理解析:https://blog.csdn.net/weixin_39494923/article/details/91534658
在这里插入图片描述 MyBatis的执行流程图。

在这里插入图片描述MyBatis的架构图

实现一个简单的拦截器

定义 Mybatis 拦截器只需要实现 Interceptor 类, 并通过注解设置需要拦截的对象即可:
type=>mybatis 的四大对象:[ParameterHandler],[ResultSetHandler],[StatementHandler],[Executor].
method => 四大对象中需要拦截的方法, 具体可以直接看源码. 例如 Executor 类的 update 方法就经常用到.
args=> 拦截方法所需要的参数, 如果不清楚的话也可以直接看源码. 例如 update 方法需要 MappedStatement 类型和 Object 类型的两个参数.
intercept(Invocation invocation)=> 此方法内写具体的拦截逻辑.
plugin()=> 没什么特殊要求的话直接写成如下形式即可, 意思是把定义的插件注册进插件链里面 (越晚注册越先执行).

需求:定义拦截器类 实现显示sql的输出量仅为1条,拦截到执行的sql再对该sql进行改写
自定义一个拦截器:

package com.wx.currencymapper.interceptor;

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.stereotype.Component;

import java.sql.Connection;
import java.util.Properties;
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class,Integer.class })})
@Component
public class SqlStatusInterceptor  implements Interceptor {
    /**拦截sql,实现显示sql的输出量仅为1条,思路:拦截到执行的sql再对该sql进行改写*/
    private static final Logger log=LoggerFactory.getLogger(SqlStatusInterceptor.class);
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        /**具体的拦截逻辑*/
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        //获取sql
        String sql= String.valueOf(metaStatementHandler.getValue("delegate.boundSql.sql"));
        //添加limit条件
        sql="select * from (" + sql + ") as temp limit 1";
        //重新设置sql
        metaStatementHandler.setValue("delegate.boundSql.sql",sql);
        return invocation.proceed();
    }

    /**
     * 把拦截器插件注册进插件链里面,越晚注册越先执行
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
        String dialect = properties.getProperty("dialect");
        log.info("mybatis intercept dialect:{}", dialect);
    }
}

不使用拦截器查询:
在这里插入图片描述
使用拦截器查询:
在这里插入图片描述
再来一个需求:自定义拦截器实现在新增/更新操作时动态为sql注入creationDate时间字段

先自定义一个注解:

package com.wx.currencymapper.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义拦截器时运行时注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface CreateTime {
    String value() default "";
}
其次编写拦截器,注意拦截的方法

```java
package com.wx.currencymapper.interceptor;



import com.wx.currencymapper.annotation.CreateTime;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Date;
import java.util.Properties;

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class,Object.class })})
@Component
public class SqlInsertInterceptor implements Interceptor {
    private static final Logger log=LoggerFactory.getLogger(SqlInsertInterceptor.class);
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取 SQL 命令
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        log.info("获取到的sql命令为:{}",sqlCommandType);
        // 获取参数
        Object parameter = invocation.getArgs()[1];

        if (parameter != null) {
            // 获取成员变量
            Field[] declaredFields = parameter.getClass().getDeclaredFields();

            for (Field field : declaredFields) {
                if (field.getAnnotation(CreateTime.class) != null) {
                    if (SqlCommandType.INSERT.equals(sqlCommandType)) { // insert 语句插入 createTime
                        field.setAccessible(true);
                        if (field.get(parameter) == null) {
                            field.set(parameter, new Date());
                        }
                    }
                }

               /* if (field.getAnnotation(UpdateTime.class) != null) { // insert 或 update 语句插入 updateTime
                    if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
                        field.setAccessible(true);
                        if (field.get(parameter) == null) {
                            field.set(parameter, new Date());
                        }
                    }
                }*/
            }
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {

    }
}
SQL的编写:

INSERT INTO orders(user_id,number,createtime,note) VALUES(1,#{number},#{createtime},#{note}) ``` 测试:
@RequestMapping("/insertOrders")
    @ResponseBody
    public void insertOrder(Orders orders){
        orders.setNumber("34");
        orders.setNote("啊哈哈啊哈");
        ordersService.insertOrders(orders);
    }

成功添加:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值