Mybatis修改sql语句
介绍
MyBatis提供了⼀种插件(plugin)的功能,虽然叫做插件,但其实这是功能。
MyBatis 允许你在已映射语句执⾏过程中的某⼀点进⾏拦截调⽤。默认情况下,MyBatis 允许使⽤插件来拦截的⽅法调⽤包括:
1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2. ParameterHandler (getParameterObject, setParameters)
3. ResultSetHandler (handleResultSets, handleOutputParameters)
4. StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接⼝的部分⽅法,⽐如update,query,commit,rollback等⽅法,还有其他接⼝的⼀些⽅法等。总体概括为:
1. 拦截执⾏器的⽅法
2. 拦截参数的处理
3. 拦截结果集的处理
4. 拦截Sql语法构建的处理
使⽤
拦截sql并增强
import ller.InterceptAnnotation;
log4j.Log4j2;
import org.utor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.flection.DefaultReflectorFactory;
import org.flection.MetaObject;
import org.flection.SystemMetaObject;
import org.springframework.stereotype.Component;
import flect.Field;
import flect.Method;
import java.sql.Connection;
import java.util.Properties;
/**
* sql,通过mybatis提供的Interceptor接⼝实现
*/
@Log4j2
@Component
//拦截StatementHandler类中参数类型为Statement的prepare⽅法(prepare=在预编译SQL前加⼊修改的逻辑)
//即拦截 Statement prepare(Connection var1, Integer var2) ⽅法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MySqlInterceptor implements Interceptor {
/**
* 拦截sql
*
* @param invocation
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) Target();
// 通过MetaObject优雅访问对象的属性,这⾥是访问statementHandler的属性;:MetaObject是Mybatis提供的⼀个⽤于⽅便、
// 优雅访问对象属性的对象,通过它可以简化代码、不需要try/catch各种reflect异常,同时它⽀持对JavaBean、Collection、Map三种类型对象的操作。
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJE new DefaultReflectorFactory());
// 先拦截到RoutingStatementHandler,⾥⾯有个StatementHandler类型的delegate变量,其实现类是BaseStatementHandler,然后就到BaseStatementHandle MappedStatement mappedStatement = (MappedStatement) Value("delegate.mappedStatement");
// id为执⾏的mapper⽅法的全路径名,如com.cq.UserMapper.insertUser,便于后续使⽤反射
String id = Id();
// sql语句类型 select、delete、insert、update
String sqlCommandType = SqlCommandType().toString();
// 数据库连接信息
// Configuration configuration = Configuration();
// ComboPooledDataSource dataSource = (Environment().getDataSource();
// JdbcUrl();
BoundSql boundSql = BoundSql();
// 获取到原始sql语句
String sql = Sql().toLowerCase();
log.info("SQL:{}", sql);
// 增强sql
// 通过反射,拦截⽅法上带有⾃定义@InterceptAnnotation注解的⽅法,并增强sql
String mSql = sqlAnnotationEnhance(id, sqlCommandType, sql);
// 直接增强sql
// mSql = sql + " limit 2";
//通过反射修改sql语句
Field field = Class().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, mSql);
log.info("增强后的SQL:{}", mSql); // 打印:增强后的SQL:select * from scenario_storage limit 2
return invocation.proceed();
}
/**
* 通过反射,拦截⽅法上带有⾃定义@InterceptAnnotation注解的⽅法,并增强sql
resultset 遍历* @param id ⽅法全路径
* @param sqlCommandType sql类型
* @param sql 所执⾏的sql语句
*/
private String sqlAnnotationEnhance(String id, String sqlCommandType, String sql) throws ClassNotFoundException {
// 通过类全路径获取Class对象
Class<?> classType = Class.forName(id.substring(0, id.lastIndexOf(".")));
// 获取当前所拦截的⽅法名称
String mName = id.substring(id.lastIndexOf(".") + 1);
// 遍历类中所有⽅法名称,并if匹配上当前所拦截的⽅法
for (Method method : DeclaredMethods()) {
if (mName.Name())) {
// 判断⽅法上是否带有⾃定义@InterceptAnnotation注解
InterceptAnnotation interceptorAnnotation = Annotation(InterceptAnnotation.class);
if (interceptorAnnotation.flag()) {
if ("SELECT".equals(sqlCommandType)) {
// 增强sql
return sql + " limit 2";
/
/ select * from scenario_storage limit 2
}
}
}
}
return sql;
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
}
测试mapper接⼝
public interface IntercaperMapper {
@InterceptAnnotation
@Select("select * from scenario_storage")
List<ScenarioStorageEntity> queryScenarioStorageAll(); }
实体类
import lombok.Data;
@Data
public class ScenarioStorageEntity {
private int id;
private String name;
}
⾃定义sql增强注解
/**
* sql增强注解
*/
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterceptAnnotation {
String value() default "";
/** true增强、false忽略 */
boolean flag() default true;
}
实践:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论