Mybatis知识Part06(Intercept方法,获取pageParams)

Intercept

实现Intercept方法
@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class, Integer.class})})
public class ExamplePlugin2 implements Interceptor {private Integer defaultPage; // 默认页码private Integer defaultPageSize; // 默认每页条数private Boolean defaultUseFlag; // 默认是否启用插件private Boolean defaultCheckFlag; // 默认是否检测页码参数private Boolean defaultCleanOrderBy; // 默认是否清除最后一个orderby语句private Logger log = Logger.getLogger(ExamplePlugin2.class);private Properties props = null;/*** 插件方法,将替代StatementHandler的prepare方法** @param invocation* @return* @throws Throwable*/@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler stmtHandler = (StatementHandler) getUnProxyObject(invocation.getTarget());MetaObject metaStatementHandler = SystemMetaObject.forObject(stmtHandler);// 获取当前调用的SQlString sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");//不是select语句if (!checkSelect(sql)) {return invocation.proceed();}BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");Object parameterObject = boundSql.getParameterObject();PageParams pageParams = getPageParamsForParamObj(parameterObject);if (pageParams == null) {// 无法获取分页参数,不进行分页return invocation.proceed();}//获取配置中是否启用分页功能Boolean useFlag = pageParams.getUseFlag() == null ? this.defaultUseFlag : pageParams.getUseFlag();if (!useFlag) {//不使用分页插件return invocation.proceed();}// 获取相关配置的参数Integer pageNum = pageParams.getPageNum() != null ? defaultPage:pageParams.getPageNum();Integer pageSize =  pageParams.getPageSize() != null ?defaultPageSize:pageParams.getPageSize();Boolean checkFlag = pageParams.getCheckFlag() != null ? defaultCheckFlag:pageParams.getCheckFlag();Boolean cleanOrderBy = pageParams.getCleanOrderBy() != null ?defaultCleanOrderBy:pageParams.getCleanOrderBy();int total = getTotal(invocation,metaStatementHandler,boundSql,cleanOrderBy);// 回填总条数到分页参数pageParams.setTotal(total);//计算总页数int totalPage = total%pageSize == 0 ?total/pageSize:total/pageSize +1;//回填总页数到分页参数pageParams.setTotalPage(totalPage);// 检查当前页码的有效性checkPage(checkFlag,pageNum,totalPage);// 修改Sqlreturn preparedSQL(invocation,metaStatementHandler,boundSql,pageNum,pageSize);}
首先从责任链中分离出最原始的StatementHandler对象,使用方法getUnProxyObjectgetUnProxyObject方法/*** 从代理都系中分离出真实对象* @param target* @return 非代理StatementHandler对象*/private Object getUnProxyObject(Object target) {MetaObject metaStatementHandler = SystemMetaObject.forObject(target);// 分离代理对象链(由于目标类可能被多个插件拦截,从而形成多次代理,通过循环可以分离出最原始的目标类)Object object = null;while (metaStatementHandler.hasGetter("h")) {object = metaStatementHandler.getValue("h");metaStatementHandler = SystemMetaObject.forObject(object);}if (object == null) {return target;}return object;}
通过这个方法,可以把JDK动态代理链上原始的StatementHandler分离出来,然后通过MetaObject对象进行绑定,可以为后续通过它分离出当前执行的SQL和参数做准备,由于拦截了所有的SQL,对于非查询(select)语句是不需要拦截的,所以需要一个判断是否是查询语句的方法checkSelect.
判断是否查询语句/*** 判断是否查询语句* @param sql --当前执行sql* @return 是否查询语句*/private boolean checkSelect(String sql) {String trimSql = sql.trim();int idx = trimSql.toLowerCase().indexOf("select");return idx ==0;}一旦判定当前不是查询语句,那么就不拦截,这样就可以直接推动责任链的千斤。如果是查询语句,则拦截这条SQL,进入下一步——分离出PageParams。
分离分页参数/*** 分离出PageParams** @param parameterObject 执行参数* @return 分页参数*/private PageParams getPageParamsForParamObj(Object parameterObject) throws IntrospectionException, InvocationTargetException, IllegalAccessException {PageParams pageParams = null;if (parameterObject == null) {return null;}//处理map参数,多个匿名参数和@Param注解参数,都是mapif (parameterObject instanceof Map) {Map paramMap = (Map) parameterObject;Set keySet = paramMap.keySet();Iterator iterator = keySet.iterator();while (iterator.hasNext()) {String key = iterator.next();Object value = paramMap.get(key);if (value instanceof PageParams) {return (PageParams) value;}}} else if (parameterObject instanceof PageParams) {// 参数是或者继承PageParamsreturn (PageParams) parameterObject;} else{ // 从POJO属性尝试读取分页参数Field[] fields = parameterObject.getClass().getDeclaredFields();// 尝试从POJO中获得类型为PageParams的属性for(Field field :fields){if(field.getType() == PageParams.class){PropertyDescriptor pd = new PropertyDescriptor(field.getName(), parameterObject.getClass());Method method = pd.getReadMethod();return (PageParams) method.invoke(parameterObject);}}}return pageParams;}
这个分离分页参数一旦分离失败,则返回null,直接推动责任链前进结束方法,如果不为null,则会分析这个分页参数,一旦这个参数配置了不启用插件,则直接推动责任链前进结束方法,而分页参数可能填值,也可能不填值。如果不填值,则使用默认的配置,否则就使用填值的内容。
求SQL所能查询的总数
/*** 获取总条数** @param invocation           入参* @param metaStatementHandler StatementHandler* @param boundSql             sql* @param cleanOrderBy         是否清除order By 语句* @return*/private int getTotal(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql, Boolean cleanOrderBy) throws Throwable {// 获取当前的mappedStatementMappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");// 配置对象Configuration cfg = mappedStatement.getConfiguration();// 当前需要执行的SQLString sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");// 去掉最后的order by 语句if (cleanOrderBy) {sql = this.cleanOrderByForSql(sql);}// 改写为统计总数的SQLString countSql = "select count(*) as total from (" + sql + ")$_paging";//获取拦截方法参数,根据插件签名,知道是Connection对象Connection connection = (Connection) invocation.getArgs()[0];PreparedStatement ps = null;int total = 0;try {// 预编译统计总数SQLps = connection.prepareStatement(countSql);// 构建统计总数BoundSqlBoundSql countBoundSql = new BoundSql(cfg, countSql, boundSql.getParameterMappings(), boundSql.getParameterObject());// 构建MyBatis的ParameterHandler用来设置总数Sql的参数ParameterHandler handler = new DefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), countBoundSql);// 设置总数SQL参数handler.setParameters(ps);// 执行查询ResultSet rs = ps.executeQuery();while (rs.next()) {total = rs.getInt("total");}} finally {// 不能关闭Connection,否则后续的SQl没办法继续if (ps != null) {ps.close();}}return total;}private String cleanOrderByForSql(String sql) {StringBuilder sb = new StringBuilder(sql);String newSql = sql.toLowerCase();// 如果没有order语句,直接返回if (newSql.indexOf("order") == -1) {return sql;}int idx = newSql.lastIndexOf("order");return sb.substring(0, idx).toString();}
从BoundSql中分离出SQl,通过分页参数判断是否需要去掉order by 语句,如果要去掉,则删除,删除它是因为它会影响SQL的执行性能,然后,通过


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部