由于我之前有一篇博客写到实现分页功能,简单的分页功能,但是如果我们有多个页面,就需要每一个页面都写一个这个麻烦的代码,显得非常的愚笨。于是出现了Mybatis分页,有点类似于Spring的AOP(这里不讲解)。
首先,我们是想一个特定查询的语句执行的时候需要进行拦截,执行分页的功能。
一般的步骤是写一个拦截类,然后注册。
我们需要一个类,这里叫做PageInterceptor
package com.interceptor;
import main.entity.Page;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import java.util.Properties;
/**
* @program: Tradingplatform
* @description:分页拦截器
* @author: Robert_Wang
* @create: 2018-09-16 19:46
**/
//拦截的类+方法(防止重载,需加上参数)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//获取拦截目标
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
//元对象, 由于我们想要获得一个 protected ResultMap,所以用反射来实现
//将元对象方便我们访问属性
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY);
//按照OGNL表达式 访问属性
//获取与xml所对应的关键信息
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
//配置文件中sql的id, 即 queryListByPage
String id = mappedStatement.getId();
//正则表达式 .+ 至少出现一个字符 $是结束符
if(id.matches(".+ByPage$")){
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
String countSql = "select count(*) from (" + sql + ")a";
Connection connection = (Connection)invocation.getArgs()[0];
PreparedStatement countStatement = connection.prepareStatement(countSql);
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(countStatement);
ResultSet rs = countStatement.executeQuery();
Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
if(rs.next()){
page.setTotalNumber(rs.getInt(1));
}
String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
//修改sql语句
metaObject.setValue("delegate.boundSql.sql",pageSql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
//返回代理类
return Plugin.wrap(o,this);
}
@Override
public void setProperties(Properties properties) {
}
}
plugin方法是返回一个代理过的类,参数是代理的对象。
setProperties是用于设置参数。是在进行注册的时候设置的参数。
interceptor是拦截的过程,也是本次的核心。
我们先明确目的:在查询语句的PreparedStatement前进行拦截,那么我们就要
获得xml中的sql语句, 执行相关的操作(查询总条数、计算多少个页面、等)
然后我们再细分这个过程慢慢解决。
拦截的类前面加一个注解,表示拦截该类时签有类型、方法的类,参数就是区分重载函数。
首先获取拦截目标 -->获取sql语句-->>进行sql相关的操作
再进行细分;
首先获取拦截目标:StatementHandler
获取sql语句之前需要在xml获取,于是需要MappedStatement这个类,这个类是需要元对象MetaObject来获取的,
而元对象是需要StatementHandler。
于是就关联起来了,其实用到的就是StatementHandler MetaObject StatementHandler最主要的三个类。
然后就是SQL操作了:先判断拦截的SQL是不是我们想要的SQL,如果是,再查询条数,设置参数值即可。
再进行细分:
查询条数:拼接SQL语句-->获取连接-->预编译-->参数设置。
其他的细枝末节就这样分了,下面不再详细分了。