自己动手写的mybatis分页插件(极其简单好用)
刚开始项目,需要用到mybatis分页,网上看了很多插件,其实实现原理基本都大同小异,但是大部分都只给了代码,注释不全,所以参考了很多篇文章(每篇文章偷一点代码,评出来自己的,半抄袭),才自己模仿着写出了一个适合自己项目的分页插件,话不多说,直接上代码,相比大部分文章,注释算很完整了
最重要的拦截器
packagecom.dnkx.interceptor;
importjava.sql.*;
importjava.util.HashMap;
importjava.util.Properties;
importorg.apache.ibatis.executor.resultset.ResultSetHandler;
importorg.apache.ibatis.executor.statement.StatementHandler;
importorg.apache.ibatis.mapping.BoundSql;
importorg.apache.ibatis.mapping.MappedStatement;
importorg.apache.ibatis.plugin.Interceptor;
importorg.apache.ibatis.plugin.Intercepts;
importorg.apache.ibatis.plugin.Invocation;
importorg.apache.ibatis.plugin.Plugin;
importorg.apache.ibatis.plugin.Signature;
importorg.apache.ibatis.reflection.MetaObject;
importorg.apache.ibatis.reflection.SystemMetaObject;
importcom.dnkx.pojo.Page;
/**
*
*分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。
*利用拦截器实现Mybatis分页的原理:
*要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句
*是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的
*prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用
*StatementHandler对象的prepare方法,即调用invocation.proceed()。
*对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设
*置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。
*
*解释一下插件中可能要用到的几个类:
*MetaObject:mybatis提供的一个基于返回获取属性值的对象的类
*BoundSql:在这个里面可以获取都要执行的sql和执行sql要用到的参数
*MappedStatement:这个可以得到当前执行的sql语句在xml文件中配置的id的值
*RowBounds:是mybatis内存分页要用到的。
*ParameterHandler:是mybatis中用来替换sql中?出现的值的.
*
*@author李小拐2016年11月9日10:59:04
*/
@Intercepts({
@Signature(type=StatementHandler.class,method="prepare",args={Connection.class}),
@Signature(type=ResultSetHandler.class,method="handleResultSets",args={Statement.class})
})
publicclassPageInterceptorimplementsInterceptor{
//拦截分页关键字
privatestaticfinalStringSELECT_ID="page";
//插件运行的代码,它将代替原有的方法,要重写最重要的intercept了
@Override
publicObjectintercept(Invocationinvocation)throwsThrowable{
if(invocation.getTarget()instanceofStatementHandler){
//这里我们有一个设定如果查询方法含有Page就进行分页其他方法无视
//所以就要获取方法名
StatementHandlerstatementHandler=(StatementHandler)invocation.getTarget();
MetaObjectmetaObject=SystemMetaObject.forObject(statementHandler);
MappedStatementmappedStatement=(MappedStatement)metaObject.getValue("delegate.mappedStatement");
StringselectId=mappedStatement.getId();
StringmethorName=selectId.substring(selectId.lastIndexOf(".")+1).toLowerCase();
//然后判断下如果含有Page就获取sql
if(methorName.contains(SELECT_ID)){
BoundSqlboundSql=(BoundSql)metaObject.getValue("delegate.boundSql");
//分页参数作为参数对象parameterObject的一个属性
Stringsql=boundSql.getSql();
System.out.println("获取到的sql:"+sql);
HashMap<String,Object>map=(HashMap<String,Object>)(boundSql.getParameterObject());
//Pagepage=(Page)(boundSql.getParameterObject());
Pagepage=(Page)map.get("page");
//重写sql
StringcountSql=concatCountSql(sql);
StringpageSql=concatPageSql(sql,page);
//System.out.println("重写的countsql:"+countSql);
System.out.println("重写的selectsql:"+pageSql);
Connectionconnection=(Connection)invocation.getArgs()[0];
PreparedStatementcountStmt=null;
ResultSetrs=null;
inttotalCount=0;
try{
countStmt=connection.prepareStatement(countSql);
rs=countStmt.executeQuery();
if(rs.next()){
totalCount=rs.getInt(1);
}
}catch(SQLExceptione){
System.out.println("Ignorethisexception"+e);
}finally{
try{
rs.close();
countStmt.close();
}catch(SQLExceptione){
System.out.println("Ignorethisexception"+e);
}
}
metaObject.setValue("delegate.boundSql.sql",pageSql);
//绑定count
page.setNumCount(totalCount);
}
}
returninvocation.proceed();
}
//拦截类型StatementHandler,重写plugin方法
@Override
publicObjectplugin(Objecttarget){
if(targetinstanceofStatementHandler){
returnPlugin.wrap(target,this);
}else{
returntarget;
}
}
@Override
publicvoidsetProperties(Propertiesproperties){
}
//改造sql
publicStringconcatCountSql(Stringsql){
//StringBuffersb=newStringBuffer("selectcount(*)from");
/*sql=sql.toLowerCase();
if(sql.lastIndexOf("order")>sql.lastIndexOf(")")){
sb.append(sql.substring(sql.indexOf("from")+4,sql.lastIndexOf("order")));
}else{
sb.append(sql.substring(sql.indexOf("from")+4));
}*/
StringBuffersb=newStringBuffer();
sql=sql.toLowerCase();
if(sql.lastIndexOf("order")>0){
sql=sql.substring(0,sql.indexOf("order"));
}
sb.append("selectcount(*)from("+sql+")tmp");
returnsb.toString();
}
publicStringconcatPageSql(Stringsql,Pagepage){
StringBuffersb=newStringBuffer();
sb.append(sql);
sb.append("limit").append(page.getPageBegin()).append(",").append(page.getPageSize());
returnsb.toString();
}
}
分页对象Page类
[java]viewplaincopy
packagecom.dnkx.pojo;
importjava.util.HashMap;
importjava.util.Map;
/**
*
*分页查询辅助类
*@author李小拐2016年11月9日13:55:37
*/
publicclassPage{
//----------分页-----------
privateintpageSize;//每页显示条数
privateintpageCurrentPage;//第几页
privateintpageBegin;//开始位置
privateintnumCount;//总条数
privateintpageTotal;//总条数
privateStringorderField="";//控制排序页面显示的
privateStringorderDirection="";
publicPage(){
}
publicPage(intpageSize,intpageCurrentPage){
super();
this.pageSize=pageSize;
this.pageCurrentPage=pageCurrentPage;
}
publicPage(Map<String,String>map){
if(map.get("pageNum")!=null){
this.setPageCurrentPage(this.pageCurrentPage=Integer.parseInt(map.get("pageNum")));//要查询的页数
}else{
this.setPageCurrentPage(1);//设置初始值
}
if(map.get("numPerPage")!=null){
this.setPageSize(Integer.parseInt(map.get("numPerPage")));//每页显示条数
}else{
this.setPageSize(5);//设置初始值
}
if(map.get("orderField")!=null){
this.setOrderField(map.get("orderField"));
}
if(map.get("orderDirection")!=null){
this.setOrderDirection(map.get("orderDirection"));
}
}
publicintgetPageCurrentPage(){
returnpageCurrentPage;
}
publicvoidsetPageCurrentPage(intpageCurrentPage){
this.pageCurrentPage=pageCurrentPage;
}
publicintgetNumCount(){
returnnumCount;
}
publicvoidsetNumCount(intnumCount){
this.numCount=numCount;
}
publicintgetPageTotal(){
return(numCount%pageSize>0)?(numCount/pageSize+1):(numCount/pageSize);
}
publicvoidsetPageTotal(intpageTotal){
this.pageTotal=pageTotal;
}
publicintgetPageSize(){
returnpageSize;
}
publicvoidsetPageSize(intpageSize){
this.pageSize=pageSize;
}
publicintgetPageBegin(){
returnpageSize*(pageCurrentPage-1);
}
publicvoidsetPageBegin(intpageBegin){
this.pageBegin=pageBegin;
}
publicStringgetOrderField(){
returnorderField;
}
publicvoidsetOrderField(StringorderField){
this.orderField=orderField;
}
publicStringgetOrderDirection(){
returnorderDirection;
}
publicvoidsetOrderDirection(StringorderDirection){
this.orderDirection=orderDirection;
}
publicstaticPagegetPage(intpageSize,intpageCurrentPage){
returnnewPage(pageSize,pageCurrentPage);
}
publicstaticPagegetPage(Mapmap){
returnnewPage(map);
}
}
Controller里面调用方式
publicStringlist(HttpServletRequestrequest){
longa=System.currentTimeMillis();
HashMap<String,Object>map=GetRequestMap.getMap(request);//自己封装的方法,取request的参数
Pagepage=Page.getPage(map);//初始化page
map.put("page",page);//把page对象放入参数集合(这个map是mybatis要用到的,包含查询条件,排序,分页等)
//控制排序页面显示的
map.put(map.get("orderField")+"",map.get("orderDirection"));
List<Employee>list=employeeService.getListPage(map);
request.setAttribute("emlist",list);
request.setAttribute("page",page);
request.setAttribute("map",map);
//取page相关属性
page.getNumCount();//总条数
page.getPageTotal();//总页数
longb=System.currentTimeMillis();
System.out.println("---------耗时:"+(b-a)+"ms");
return"basic/employee_list";
}
最后,spring里面配置插件
<beanid="PageInterector"class="com.dnkx.interceptor.PageInterceptor"></bean> <!--spring和MyBatis完美整合,不需要mybatis的配置映射文件--> <beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"> <propertyname="dataSource"ref="dataSource"/> <!--自动扫描mapping.xml文件--> <propertyname="mapperLocations"value="classpath:com/dnkx/mapping/*.xml"></property> <propertyname="plugins"> <refbean="PageInterector"/> </property> </bean>
好了,到此结束,本文仅供参考!也期待大神提意见