一、拦截器实现
1.原理
在mybatis 运行过程中拦截执行对象,获得sql信息,将分页信息添加到sql语句中,然后放行mybatis的执行过程
2.了解一点mybatis源码
首先我们需要明白要拦截的对象是处理对象(Statement),拦截的时机应该是sql执行之前,
所以我们应该拦截的是:
@Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})
type 表示需要拦截的类, method 表示需要拦截的方法, args 表示参数
我们这里拦截主要是(其实这里拦截到的是而是RoutingStatementHandler,它会通过判断来决定实例化那个StatementHandler, BaseStatementHandler只是这里Statement的实现类) BaseStatementHandler ,它实现了Statement。 BaseStatementHandler中通过instantiateStatement 方法来获取statement对象,instantiateStatement的具体实现在PreparedStatementHandler。
上面的叙述可能会有偏差,源码的理解有待提高,这里看源码的主要目的是为了清楚sql的具体信息在哪里,比如BaseStatementHandler下的mappedStatement对应的就是mapper 中xml的信息,BoundSql对应一些sql信息。
为了在代码中获取到sql的具体信息我们需要借助MetaObject这个类,它可以使我们不需要写反射代码获取到一些必要的信息。
3.具体代码编写过程
首先,将需要分页的方法名以”ByPage“结尾 或者什么标志标记一下,以确保自己能在后面将这些方法过滤出来
然后,编写拦截器:
page类:
public class Page {
private int totalNumber;
private int currentPage;
private int totalPage;
private int pageNumber = 5;
//从第几条开始
private int dbIndex;
//取几条
private int dbNumber;
public void count(){
int totlaPageTemp = this.totalNumber / this.pageNumber;
int plus = (this.totalNumber%this.pageNumber)==0?0:1;
totlaPageTemp += plus;
if( totlaPageTemp <= 0 ){
totlaPageTemp = 1;
}
this.totalPage = totlaPageTemp;
if( this.totalPage < this.currentPage ){
this.currentPage = this.totalPage;
}
if( this.currentPage < 1 ){
this.currentPage = 1;
}
this.dbIndex = (this.currentPage-1)*this.pageNumber;
this.dbNumber = this.pageNumber;
}
public int getTotalNumber() {
return totalNumber;
}
public void setTotalNumber(int totalNumber) {
this.totalNumber = totalNumber;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public int getDbIndex() {
return dbIndex;
}
public void setDbIndex(int dbIndex) {
this.dbIndex = dbIndex;
}
public int getDbNumber() {
return dbNumber;
}
public void setDbNumber(int dbNumber) {
this.dbNumber = dbNumber;
}
@Override
public String toString() {
return "Page{" +
"totalNumber=" + totalNumber +
", currentPage=" + currentPage +
", totalPage=" + totalPage +
", pageNumber=" + pageNumber +
", dbIndex=" + dbIndex +
", dbNumber=" + dbNumber +
'}';
}
}
/**
* mybatis 分页拦截器
*/
@Intercepts({@Signature(type=StatementHandler.class,method = "prepare",args={Connection.class,Integer.class})})
public class PageInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler,SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
/*
从RoutingStatementHandler中获得处理对象PreparedStatementHandler,从这个对象中获取Mapper中的xml信息
* */
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//配置文件中sql语句的id
String id = mappedStatement.getId();
//判断是否是需要分页的方法
if( id.matches(".+ByPage$") ){
//获得sql的参数信息
BoundSql boundSql = statementHandler.getBoundSql();
Map<String, Object> parameter = (Map<String, Object>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
//原始的sql
String sql = boundSql.getSql();
System.out.println("未改造的sql语句:"+sql);
//组装page中的信息,查询总条数
String countSql = "select count(*) from ("+sql+")a";
System.out.println("查询总条数sql语句:"+countSql);
Connection connection = (Connection)invocation.getArgs()[0];
PreparedStatement statement = connection.prepareStatement(countSql);
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(statement);
ResultSet rs = statement.executeQuery();
if( rs.next() ){
int total = rs.getInt(1);
page.setTotalNumber(total);
System.out.println("总条数:"+total);
}else{
System.out.println("未查询到数据量");
}
page.count();
//改造后的sql语句
String pageSql = sql + " limit "+ page.getDbIndex()+" , "+page.getDbNumber();
System.out.println("改造后的sql语句:"+pageSql);
metaObject.setValue("delegate.boundSql.sql",pageSql);
}
//放行
return invocation.proceed();
}
//被拦截的请求
public Object plugin(Object o) {
return Plugin.wrap(o,this);
}
public void setProperties(Properties properties) {
//这里可以拿到配置文件中 plugin 中的propertie的值
}
}
最后,在配置文件中注册拦截器
<plugins>
<plugin interceptor="per.ly.interceptor.PageInterceptor" />
</plugins>
4.总结
使用不够快捷。
二、pageHelper
参考自:https://www.cnblogs.com/shanheyongmu/p/5864047.html
出现错误一:https://blog.csdn.net/s592652578/article/details/78179998?locationNum=4&fps=1
出现错误二:https://blog.csdn.net/u011560753/article/details/78750119
1、添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
2、在mybatis配置文件中配置
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
</plugin>
</plugins>
3、实现简单分页
PageHelper.startPage(pagenum,pagesize);
4、总结
只能对查询出的结果进行分页,可能无法满足要求。