其中包含如下几个要点:获取mappedStatement、boundSql和重置mappedStatement。
/** Copyright (c) 2018-2028, Chill Zhuang All rights reserved.** Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions are met:** Redistributions of source code must retain the above copyright notice,* this list of conditions and the following disclaimer.* Redistributions in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* Neither the name of the dreamlu.net developer nor the names of its* contributors may be used to endorse or promote products derived from* this software without specific prior written permission.* Author: Chill 庄骞 (smallchill@163.com)*/package org.springblade.core.mp.plugins;import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;import lombok.extern.slf4j.Slf4j;import net.sf.jsqlparser.expression.operators.relational.EqualsTo;import net.sf.jsqlparser.parser.CCJSqlParserUtil;import net.sf.jsqlparser.schema.Column;import net.sf.jsqlparser.schema.Table;import net.sf.jsqlparser.statement.Statement;import net.sf.jsqlparser.statement.select.Join;import net.sf.jsqlparser.statement.select.PlainSelect;import net.sf.jsqlparser.statement.select.Select;import net.sf.jsqlparser.statement.select.SelectBody;import org.apache.ibatis.cache.CacheKey;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.SqlCommandType;import org.apache.ibatis.mapping.SqlSource;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.reflection.DefaultReflectorFactory;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.factory.DefaultObjectFactory;import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.springblade.core.mp.annotation.SqlAssets;import org.springframework.stereotype.Component;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.sql.SQLException;import java.util.Collections;import java.util.Properties;/*** 租户拦截器** @author Chill*/@Slf4j@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})@Componentpublic class CdosAssetInterceptor extends JsqlParserSupport implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取拦截的参数Object[] args = invocation.getArgs();MappedStatement mappedStatement = (MappedStatement) args[0];// 如果不是select方法,则继续执行if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {return invocation.proceed();}// 获取执行方法的包名和方法名String namespace = mappedStatement.getId();String className = namespace.substring(0, namespace.lastIndexOf("."));String methodName = namespace.substring(namespace.lastIndexOf(".") + 1, namespace.length());Method[] methods = Class.forName(className).getMethods();SqlAssets annotation = null;for (Method method : methods) {if (method.getName().equals(methodName)) {//获取注解 来判断是不是要储存sqlannotation = method.getAnnotation(SqlAssets.class);break;}}if (annotation != null) {// 获得关联的参数String tableName = annotation.tableName();String fieldName = annotation.fieldName();// 获取调用方法的参数列表Object parameter = null;if (args.length > 1) {parameter = args[1];}BoundSql boundSql = mappedStatement.getBoundSql(parameter);// PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);String originalSql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();// mpBs.sql(parserSingle(mpBs.sql(), null));log.info("");if (parameterObject != null) {// 解析SqlStatement statement = CCJSqlParserUtil.parse(originalSql);SelectBody selectBody = ((Select) statement).getSelectBody();PlainSelect plainSelect = (PlainSelect) selectBody;// todo: 字段冲突,新加的tb_asset表如果和旧表字段冲突怎么办// 修改joinsTable assetTable = new Table("tb_asset");Table oldTable = new Table(tableName);Join join = new Join();join.setInner(true);join.setRightItem(assetTable);EqualsTo equalsTo = new EqualsTo();equalsTo.setLeftExpression(new Column(assetTable, "id"));equalsTo.setRightExpression(new Column(oldTable, fieldName));join.setOnExpression(equalsTo);if (plainSelect.getJoins() != null) {plainSelect.getJoins().add(join);} else {plainSelect.setJoins(Collections.singletonList(join));}String newSql = statement.toString();resetSql2Invocation(invocation, newSql);}}// List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// Class<?>[] parameterClasses = parameterMappings.parallelStream().map(ParameterMapping::getJavaType).toArray(Class<?>[]::new);// // 获取方法// Class<?> aClass = Class.forName(className);// Method method = aClass.getMethod(methodName, parameterClasses);return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Interceptor.super.plugin(target);}@Overridepublic void setProperties(Properties properties) {Interceptor.super.setProperties(properties);}/*** 包装sql后,重置到invocation中** @param invocation* @param sql* @throws SQLException*/private void resetSql2Invocation(Invocation invocation, String sql) throws SQLException {final Object[] args = invocation.getArgs();MappedStatement statement = (MappedStatement) args[0];Object parameterObject = args[1];BoundSql boundSql = statement.getBoundSql(parameterObject);MappedStatement newStatement = newMappedStatement(statement, new BoundSqlSqlSource(boundSql));MetaObject msObject = MetaObject.forObject(newStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());msObject.setValue("sqlSource.boundSql.sql", sql);args[0] = newStatement;}private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {MappedStatement.Builder builder =new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator());if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {StringBuilder keyProperties = new StringBuilder();for (String keyProperty : ms.getKeyProperties()) {keyProperties.append(keyProperty).append(",");}keyProperties.delete(keyProperties.length() - 1, keyProperties.length());builder.keyProperty(keyProperties.toString());}builder.timeout(ms.getTimeout());builder.parameterMap(ms.getParameterMap());builder.resultMaps(ms.getResultMaps());builder.resultSetType(ms.getResultSetType());builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();}// 定义一个内部辅助类,作用是包装sqlclass BoundSqlSqlSource implements SqlSource {private BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return boundSql;}}}
