MyBatis提供了Interceptor接口使得我们在执行SQL之前可以进行额外操作,比如:记录操作人和更新时间,记录日志等等。

public interface Interceptor {
	//主要是这个拦截方法,var1包含数据库的连接信息,传输的实体类,动态生成的SQL,参数等等
    Object intercept(Invocation var1) throws Throwable;
	// 下面两个有默认实现
    default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    default void setProperties(Properties properties) {
    }
}

MyBatis的SQL拦截器_bc

1.Executor:拦截执行器的方法。
2.ParameterHandler:拦截参数的处理。
3.ResultHandler:拦截结果集的处理。
4.StatementHandler:拦截Sql语法构建的处理。

自定义拦截器,实现Interceptor、使用Intercepts注解配置你想要的拦截点和将该拦截器注入Spring中

@Intercepts({@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class})})

type:主要是以上4种

method:对应类里的方法

MyBatis的SQL拦截器_bc_02

args:则是对应的方法参数

MyBatis的SQL拦截器_apache_03

我这里功能实现是自动添加创建人、更新人、操作时间等,由于invocation提供的对象没有提供setter方法,只能通过反射对对象进行设值。对修改的如果想要修改SQL和添加参数值,要同时修改invocation.target的这两个boundSql。

MyBatis的SQL拦截器_bc_04

为什么要同时修改这两个呢?

因为最外层的boundSql是用来生成PreparementStatement,而ParameterHandler里的boundSql,是用来设置占位符的值,跟踪源码执行到MybatisDefaultParameterHandler这个类的setParameters

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.baomidou.mybatisplus.core;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

public class MybatisDefaultParameterHandler extends DefaultParameterHandler {
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final MappedStatement mappedStatement;
    private final Object parameterObject;
    private final BoundSql boundSql;
    private final Configuration configuration;

    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
        // 根据this.boundSql.getParameterMappings()获取parameterMappings来控制参数
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();
                    Object value;
                    if (this.boundSql.hasAdditionalParameter(propertyName)) {
                        value = this.boundSql.getAdditionalParameter(propertyName);
                    } else if (this.parameterObject == null) {
                        value = null;
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
                    	// this.parameterObject实际参数值
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);
                        value = metaObject.getValue(propertyName);
                    }

                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (SQLException | TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    }
                }
            }
        }

    }
}
package com.lzl.configuration;

import cn.hutool.core.lang.Snowflake;
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
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.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Timestamp;
import java.util.Properties;

@Component
@Intercepts({@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class})})
public class OperatingInformationInterceptor implements Interceptor {
    private static Logger logger = LoggerFactory.getLogger(OperatingInformationInterceptor.class);
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        RoutingStatementHandler target = (RoutingStatementHandler) (invocation.getTarget());
        Class<? extends RoutingStatementHandler> targetClass = target.getClass();
        Field delegate = targetClass.getDeclaredField("delegate");
        delegate.setAccessible(true);
        PreparedStatementHandler preparedStatementHandler = (PreparedStatementHandler) delegate.get(target);
        Class<?> preparedStatementHandlerSuperclass = preparedStatementHandler.getClass().getSuperclass();
        Field mappedStatementField = preparedStatementHandlerSuperclass.getDeclaredField("mappedStatement");
        mappedStatementField.setAccessible(true);
        MappedStatement mappedStatement = (MappedStatement) mappedStatementField.get(preparedStatementHandler);

        if (mappedStatement.getSqlCommandType().equals(SqlCommandType.INSERT)){
            Object parameterObject = target.getParameterHandler().getParameterObject();
            Class<?> clazz = parameterObject.getClass();
            Field iid = null;
            try{
                iid = clazz.getDeclaredField("iid");
                iid.setAccessible(true);
                long aLong = iid.getLong(parameterObject);
            }catch (NoSuchFieldException noSuchFieldException){
                logger.error(noSuchFieldException.getLocalizedMessage());
            }catch (IllegalArgumentException illegalArgumentException){
                logger.error("iid is null");
                Connection connection = (Connection) invocation.getArgs()[0];
                PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO `dev`.`base_info`(`id`, `create_time`, `create_by`, `update_time`, `update_by`) VALUES (?, ?, 1, ?, 1)");
                long id = new Snowflake().nextId();
                preparedStatement.setLong(1, id);
                preparedStatement.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
                preparedStatement.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
                preparedStatement.execute();
                iid.set(parameterObject, Long.valueOf(id));
                Field boundSql = preparedStatementHandlerSuperclass.getDeclaredField("boundSql");

                Field parameterHandler = preparedStatementHandlerSuperclass.getDeclaredField("parameterHandler");
                parameterHandler.setAccessible(true);
                Object parameterHandlerObject = parameterHandler.get(preparedStatementHandler);
                Field boundSqlOfParameterHandler = parameterHandlerObject.getClass().getDeclaredField("boundSql");
                boundSqlOfParameterHandler.setAccessible(true);
                boundSqlOfParameterHandler.set(parameterHandlerObject,mappedStatement.getBoundSql(parameterObject));
                boundSql.setAccessible(true);
                boundSql.set(preparedStatementHandler, mappedStatement.getBoundSql(parameterObject));
                logger.error(illegalArgumentException.getLocalizedMessage());
            }
        }
        return invocation.proceed();
    }

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

    @Override
    public void setProperties(Properties properties) {
        System.out.println(properties);
    }
}