自己动手写mybatis分页插件

来源:互联网 发布:php new 对象 编辑:程序博客网 时间:2024/04/30 02:48

刚开始项目,需要用到mybatis分页,网上看了很多插件,其实实现原理基本都大同小异,但是大部分都只给了代码,注释不全,所以参考了很多篇文章(每篇文章偷一点代码,评出来自己的,半抄袭),才自己模仿着写出了一个适合自己项目的分页插件,话不多说,直接上代码,相比大部分文章,注释算很完整了

最重要的拦截器

package com.dnkx.interceptor;import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.HashMap;import java.util.List;import java.util.Properties;import org.apache.ibatis.executor.ErrorContext;import org.apache.ibatis.executor.ExecutorException;import org.apache.ibatis.executor.resultset.ResultSetHandler;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.ParameterMode;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Plugin;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import org.apache.ibatis.reflection.property.PropertyTokenizer;import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;import org.apache.ibatis.session.Configuration;import org.apache.ibatis.type.TypeHandler;import org.apache.ibatis.type.TypeHandlerRegistry;import com.dnkx.pojo.Page;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** *  * 分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。  * 利用拦截器实现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})})public class PageInterceptor implements Interceptor{private final Logger logger = LoggerFactory.getLogger(getClass()); //拦截分页关键字 private static final String SELECT_ID="page"; //插件运行的代码,它将代替原有的方法,要重写最重要的intercept了@Overridepublic Object intercept(Invocation invocation) throws Throwable {if (invocation.getTarget() instanceof StatementHandler) {//这里我们有一个设定  如果查询方法含有Page 就进行分页 其他方法无视//所以就要获取方法名StatementHandler statementHandler=(StatementHandler)invocation.getTarget();MetaObject metaObject=SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement=(MappedStatement)metaObject.getValue("delegate.mappedStatement");String selectId=mappedStatement.getId();String methorName=selectId.substring(selectId.lastIndexOf(".")+1).toLowerCase();//然后判断下 如果含有Page 就获取sqlif(methorName.contains(SELECT_ID)){BoundSql boundSql=(BoundSql)metaObject.getValue("delegate.boundSql");//分页参数作为参数对象parameterObject的一个属性  String sql=boundSql.getSql();//logger.info("获取到的sql:{}",sql);HashMap<String, Object> map=(HashMap<String, Object>)(boundSql.getParameterObject());//Page page=(Page)(boundSql.getParameterObject());Page page=(Page)map.get("page");// 重写sql          String countSql=concatCountSql(sql);        String pageSql=concatPageSql(sql,page);                //logger.info("重写的 select sql:{}",pageSql);                Connection connection = (Connection) invocation.getArgs()[0];                  PreparedStatement countStmt = null;          ResultSet rs = null;          int totalCount = 0;          try {             countStmt = connection.prepareStatement(countSql);             setParameters(countStmt,mappedStatement,boundSql,boundSql.getParameterObject());            rs = countStmt.executeQuery();              if (rs.next()) {                  totalCount = rs.getInt(1);            }                     } catch (SQLException e) {              logger.error("SQLException",e);        } finally {              try {                  rs.close();                  countStmt.close();              } catch (SQLException e) {                 logger.error("SQLException", e);            }          }                  metaObject.setValue("delegate.boundSql.sql", pageSql);                          //绑定count        page.setNumCount(totalCount);}}return invocation.proceed();}// 拦截类型StatementHandler,重写plugin方法@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}else {return target;}}@Overridepublic void setProperties(Properties properties) {}//改造sqlpublic String concatCountSql(String sql){        //StringBuffer sb=new StringBuffer("select count(*) 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));        }*/StringBuffer sb=new StringBuffer();sql=sql.toLowerCase();        if(sql.lastIndexOf("order by")>0){            sql=sql.substring(0,sql.indexOf("order by"));        }        sb.append("select count(*) from ("+sql+") tmp");        return sb.toString();    }        public String concatPageSql(String sql,Page page){        StringBuffer sb=new StringBuffer();        sb.append(sql);        sb.append(" limit ").append(page.getPageBegin()).append(" , ").append(page.getPageSize());        return sb.toString();    }        /** * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler * @param ps * @param mappedStatement * @param boundSql * @param parameterObject * @throws SQLException */private void setParameters(PreparedStatement ps,MappedStatement mappedStatement,BoundSql boundSql,Object parameterObject) throws SQLException {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {Configuration configuration = mappedStatement.getConfiguration();TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;String propertyName = parameterMapping.getProperty();PropertyTokenizer prop = new PropertyTokenizer(propertyName);if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else if (boundSql.hasAdditionalParameter(propertyName)) {value = boundSql.getAdditionalParameter(propertyName);} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {value = boundSql.getAdditionalParameter(prop.getName());if (value != null) {value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));}} else {value = metaObject == null ? null : metaObject.getValue(propertyName);}TypeHandler typeHandler = parameterMapping.getTypeHandler();if (typeHandler == null) {throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement "+ mappedStatement.getId());}typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());}}}}}


分页对象Page类

package com.dnkx.pojo;import java.util.HashMap;import java.util.Map;/** *  * 分页查询辅助类 * @author 李小拐 2016年11月9日 13:55:37 */public class Page {//----------分页-----------private int pageSize;//每页显示条数private int pageCurrentPage;//第几页private int pageBegin;//开始位置private int numCount;//总条数private int pageTotal;//总条数private String orderField = "";//控制排序页面显示的private String orderDirection = "";public Page(){}public Page(int pageSize, int pageCurrentPage) {super();this.pageSize = pageSize;this.pageCurrentPage = pageCurrentPage;}public Page(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"));}}public int getPageCurrentPage() {return pageCurrentPage;}public void setPageCurrentPage(int pageCurrentPage) {this.pageCurrentPage = pageCurrentPage;}public int getNumCount() {return numCount;}public void setNumCount(int numCount) {this.numCount = numCount;}public int getPageTotal() {return (numCount%pageSize>0)?(numCount/pageSize+1):(numCount/pageSize);}public void setPageTotal(int pageTotal) {this.pageTotal = pageTotal;}public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize = pageSize;}public int getPageBegin() {return pageSize*(pageCurrentPage-1);}public void setPageBegin(int pageBegin) {this.pageBegin = pageBegin;}public String getOrderField() {return orderField;}public void setOrderField(String orderField) {this.orderField = orderField;}public String getOrderDirection() {return orderDirection;}public void setOrderDirection(String orderDirection) {this.orderDirection = orderDirection;}public static Page getPage(int pageSize, int pageCurrentPage){return new Page(pageSize,pageCurrentPage);}public static Page getPage(Map map){return new Page(map);}}

Controller里面调用方式

public String list(HttpServletRequest request) {long a=System.currentTimeMillis();HashMap<String,Object> map=GetRequestMap.getMap(request);//自己封装的方法,取request的参数Page page= Page.getPage(map);//初始化pagemap.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();//总页数long b=System.currentTimeMillis();System.out.println("---------耗时:"+(b-a)+"ms");return "basic/employee_list";}

最后,spring里面配置插件

 <bean id="PageInterector" class="com.dnkx.interceptor.PageInterceptor"></bean>              <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">            <property name="dataSource" ref="dataSource" />            <!-- 自动扫描mapping.xml文件 -->            <property name="mapperLocations" value="classpath:com/dnkx/mapping/*.xml"></property>              <property name="plugins">            <ref bean="PageInterector"/>            </property>        </bean>  

好了,到此结束,本文仅供参考!也期待大神提意见






0 0
原创粉丝点击