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) {
}
}
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:对应类里的方法
args:则是对应的方法参数
我这里功能实现是自动添加创建人、更新人、操作时间等,由于invocation提供的对象没有提供setter方法,只能通过反射对对象进行设值。对修改的如果想要修改SQL和添加参数值,要同时修改invocation.target的这两个boundSql。
为什么要同时修改这两个呢?
因为最外层的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);
}
}