Mybatis拦截器实现限制查询条数
问题:查询结果过大的sql导致服务慢,系统不稳定?
解决思路:拦截sql,对sql进行修改,添加limit条件,限制查询结果的条数。
实现:
1、使用Mybatis拦截器。
将拦截器类交给spring管理,使用配置文件、配置类、或直接使用@Component注解均可。
目的都是将拦截器类注入spring容器中。
任选一种配置方式
1.1 配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> <plugins><plugin interceptor="com.qxy.mybatis.interceptor.TableLimitInterceptor"/></plugins></configuration>
1.2 配置类:
@Configuration
public class MyBatisConfiguration {@BeanTableLimitInterceptor tableLimitInterceptor(){return new TableLimitInterceptor();}
}
1.3 @Component注解:(直接在拦截器类上添加注解)
2、Mybatis拦截器实现代码
实现org.apache.ibatis.plugin.Interceptor
接口,重写以下方法:
public interface Interceptor {// 拦截器拦截后对象后,执行自己的业务逻辑Object intercept(Invocation invocation) throws Throwable;// 判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。Object plugin(Object target);// 可给拦截器设置一些变量对象void setProperties(Properties properties);}
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class TableLimitInterceptor implements Interceptor {private static final Logger log = LoggerFactory.getLogger(ChannelBusiProcessorFactory.class);// 拦截器拦截后对象后,执行自己的业务逻辑@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 入参invocation指拦截到的对象StatementHandler handler = (StatementHandler) invocation.getTarget();BoundSql boundSql = handler.getBoundSql();String sql = boundSql.getSql();MySqlStatementParser mySqlStatementParser = new MySqlStatementParser(sql);SQLStatement statement = mySqlStatementParser.parseStatement();if (statement instanceof SQLSelectStatement) {SQLSelect selectQuery = ((SQLSelectStatement) statement).getSelect();MySqlSelectQueryBlock sqlSelectQuery = (MySqlSelectQueryBlock) selectQuery.getQuery();String tableName = sqlSelectQuery.getFrom().toString();SQLLimit limit = sqlSelectQuery.getLimit();// 不拦截 带有limit 并且 表名中含有 dict的sqlif(null == limit && !tableName.contains("dict") ){SQLLimit sqlLimit = new SQLLimit();sqlLimit.setRowCount(1000);sqlSelectQuery.setLimit(sqlLimit);String newSql = sqlSelectQuery.toString();// 修改 sqlReflectUtil.setFieldValue(boundSql, "sql", newSql);}}return invocation.proceed(); //程序继续运行}//判断是否拦截这个类型对象(根据@Intercepts注解决定),然后决定是返回一个代理对象还是返回原对象。//每经过一个拦截器对象都会调用插件的plugin方法,也就是说,该方法会调用4次。根据@Intercepts注解来决定是否进行拦截处理。@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}}// 可给拦截器设置一些变量对象@Overridepublic void setProperties(Properties properties) {}
}
3、拦截器知识:
3.1 MyBatis四大核心对象(四个可以被拦截的对象):
ParameterHandler: 处理sql 参数对象
ResultsetHandler: 处理sql 返回结果集
StatementHandler: 数据库的处理对象,执行sql语句
Executor: MyBatis执行器, 用于执行增删改查操作
3.2
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Intercepts
Intercepts注解需要一个Signature(拦截点)参数数组。通过Signature来指定拦截哪个对象里面的哪个方法。
@Intercepts:标识该类是一个拦截器;
@Signature:指明自定义拦截器需要拦截哪一个类型,哪一个方法;
type:上述四种对象类型中的一种;
method:对应接口中的哪类方法(因为可能存在重载方法);
args:对应哪一个方法的入参;
(注:type、method、args三个值共同来确定需要拦截的具体方法)
拦截类型 | 拦截方法 |
---|---|
Executor | update, query, flushStatements, commit, rollback,getTransaction, close, isClosed |
ParameterHandler | getParameterObject, setParameters |
StatementHandler | prepare, parameterize, batch, update, query |
ResultSetHandler | handleResultSets, handleOutputParameters |
总结:拦截器可拦截上述的任意一个或多个方法, 拦截到方法后,在方法中拿到对应的参数,可根据自己的业务逻辑对参数进行修改,实现自己的业务,非常的灵活。可同时使用多个拦截器,拦截多个不同的方法不会产生冲突,并且多个拦截器可同时拦截同一个方法,此时多个拦截器会依次执行,这里就要考虑拦拦截器执行的先后顺序了。