MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL

Posted ykzhen2015

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL相关的知识,希望对你有一定的参考价值。

1、概述:
经过努力写了个分页插件,目前支持mysql和Oracle。它的使用应该是目前为止最简单的,对于PageHelper据原作者描述使用了ThreadLocal等技术,避免多线程的不一致,PageHelper实现较为复杂,其次也有其定义的startPage,endPage这样的开关代码,造成代码侵入,而没有屏蔽PageHelper导致代码污染。

我的插件十分简单易用,代码污染性极小,也无需考虑多线程的问题,反正都是一个事务中处理的,其次使用方法支持四种,十分灵活。


2、插件代码,配置和简易的POJO

我们先看看插件的代码:

package com.learn.mybatis.plugin;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.transaction.NotSupportedException;

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.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.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.log4j.Logger;

import com.learn.mybatis.chapter1.main.MyBatisExample;

/**
 *
 * @author ykzhen2015
 */
@Intercepts({
    @Signature(type = StatementHandler.class,
            method = "prepare",
            args = {Connection.class})})
public class PagingPlugin implements Interceptor {

	/**
	 * 插件默认参数,可配置
	 */
    private Integer defaultPage; //默认页码
    private Integer defaultPageSize;//默认每页条数
    private Boolean defaultUseFlag; //默认是否启用插件
    private Boolean defaultCheckFlag; //默认是否检测页码参数
    private Boolean defaultCleanOrderBy; //默认是否清除最后一个order by 后的语句

    private static final String DB_TYPE_MYSQL = "mysql";
    private static final String DB_TYPE_ORACLE = "oracle";

    /**
     * 插件实现方法
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler stmtHandler = (StatementHandler) getUnProxyObject(invocation.getTarget());

        MetaObject metaStatementHandler = SystemMetaObject.forObject(stmtHandler);
        String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");

        MappedStatement mappedStatement =  (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
        String dbType = this.getDataSourceType(mappedStatement);

        //不是select语句.
        if (!this.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.getPage() == null? defaultPage : pageParams.getPage();
        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 = this.getTotal(invocation, metaStatementHandler, boundSql, cleanOrderBy, dbType);

        pageParams.setTotal(total);
        //计算总页数.
        int totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        pageParams.setTotalPage(totalPage);
        //检查当前页码的有效性.
        this.checkPage(checkFlag, pageNum, totalPage);
        //修改sql
        return this.preparedSQL(invocation, metaStatementHandler, boundSql, pageNum, pageSize, dbType);
    }


    /***
     * 分离出分页参数.
     * @param parameterObject --执行参数
     * @return 分页参数
     * @throws Exception
     */
    public PageParams getPageParamsForParamObj(Object parameterObject) throws Exception {
    	PageParams pageParams = null;
    	if (parameterObject == null) {
    		return null;
    	}
    	//处理map参数和@Param注解参数,都是MAP
    	if (parameterObject instanceof Map) {
    		@SuppressWarnings("unchecked")
			Map<String, Object> paramMap = (Map<String, Object>) parameterObject;
    		Set<String> keySet = paramMap.keySet();
    		Iterator<String> 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) { //参数POJO继承了PageParams
        	return (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;
    }


    /**
     * 判断是否sql语句.
     * @param sql
     * @return
     */
    private boolean checkSelect(String sql) {
        String trimSql = sql.trim();
        int idx = trimSql.toLowerCase().indexOf("select");
        return idx == 0;
    }

    /**
     * 检查当前页码的有效性.
     * @param checkFlag
     * @param pageNum
     * @param pageTotal
     * @throws Throwable
     */
    private void checkPage(Boolean checkFlag, Integer pageNum, Integer pageTotal) throws Throwable  {
        if (checkFlag) {
            //检查页码page是否合法.
            if (pageNum > pageTotal) {
                throw new Exception("查询失败,查询页码【" + pageNum + "】大于总页数【" + pageTotal + "】!!");
            }
        }
    }


    /**
     * 预编译改写后的SQL,并设置分页参数
     * @param invocation
     * @param metaStatementHandler
     * @param boundSql
     * @param pageNum
     * @param pageSize
     * @param dbType
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private Object preparedSQL(Invocation invocation, MetaObject metaStatementHandler, BoundSql boundSql, int pageNum, int pageSize, String dbType) throws Exception {
        //获取当前需要执行的SQL
        String sql = boundSql.getSql();
        String newSql = this.getPageDataSQL(sql, dbType);
        //修改当前需要执行的SQL
        metaStatementHandler.setValue("delegate.boundSql.sql", newSql);
        //执行编译,这里相当于StatementHandler执行了prepared()方法,这个时候,就剩下2个分页参数没有设置。
        Object statementObj = invocation.proceed();
        //设置两个分页参数。
        this.preparePageDataParams((PreparedStatement)statementObj, pageNum, pageSize, dbType);
        return statementObj;
    }



    /**
     * 获取综述.
     *
     * @param ivt Invocation
     * @param metaStatementHandler statementHandler
     * @param boundSql sql
     * @param cleanOrderBy 是否清除order by语句
     * * @param dbType
     * @return sql查询总数.
     * @throws Throwable 异常.
     */
    private int getTotal(Invocation ivt, MetaObject metaStatementHandler, BoundSql boundSql, Boolean cleanOrderBy, String dbType) throws Throwable {
        //获取当前的mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
        //配置对象
        Configuration cfg = mappedStatement.getConfiguration();
        //当前需要执行的SQL
        String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
        //去掉最后的order by语句
        if (cleanOrderBy) {
            sql = this.cleanOrderByForSql(sql);
        }
        String countSql = this.getTotalSQL(sql, dbType);
        //获取拦截方法参数,根据插件签名,知道是Connection对象.
        Connection connection = (Connection) ivt.getArgs()[0];
        PreparedStatement ps = null;
        int total = 0;
        try {
            //预编译统计总数SQL
            ps = connection.prepareStatement(countSql);
            //构建统计总数SQL
            BoundSql 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.isClosed()) {
                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();
    }

    /**
     * 从代理对象中分离出真实对象.
     *
     * @param ivt --Invocation
     * @return 非代理StatementHandler对象
     */
    private Object getUnProxyObject(Object target) {
        MetaObject metaStatementHandler = SystemMetaObject.forObject(target);
        // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过循环可以分离出最原始的的目标类)
        Object object = null;
        while (metaStatementHandler.hasGetter("h")) {
            object = metaStatementHandler.getValue("h");
        }
        if (object == null) {
            return target;
        }
        return object;
    }

    /**
     * 生成代理对象
     * @param statementHandler 原始对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object statementHandler) {
        return Plugin.wrap(statementHandler, this);
    }

    /**
     * 设置插件配置参数。
     * @param props
     */
    @Override
    public void setProperties(Properties props) {
        String strDefaultPage = props.getProperty("default.page", "1");
        String strDefaultPageSize = props.getProperty("default.pageSize", "50");
        String strDefaultUseFlag = props.getProperty("default.useFlag", "false");
        String strDefaultCheckFlag = props.getProperty("default.checkFlag", "false");
        String StringDefaultCleanOrderBy = props.getProperty("default.cleanOrderBy", "false");

        this.defaultPage = Integer.parseInt(strDefaultPage);
        this.defaultPageSize = Integer.parseInt(strDefaultPageSize);
        this.defaultUseFlag = Boolean.parseBoolean(strDefaultUseFlag);
        this.defaultCheckFlag = Boolean.parseBoolean(strDefaultCheckFlag);
        this.defaultCleanOrderBy = Boolean.parseBoolean(StringDefaultCleanOrderBy);
    }

    /**
     * TODO
     * 计算总数的SQL,
     * 这里需要根据数据库的类型改写SQL,目前支持MySQL和Oracle
     * @param currSql —— 当前执行的SQL
     * @return 改写后的SQL
     * @throws NotSupportedException
     */
    private String getTotalSQL(String currSql, String dbType) throws NotSupportedException {
    	if (DB_TYPE_MYSQL.equals(dbType)) {
    	    return  "select count(*) as total from (" + currSql + ") $_paging";
    	} else if (DB_TYPE_ORACLE.equals(dbType)) {
    		return "select count(*) as total from (" + currSql +")";
    	} else {
    		throw new NotSupportedException("当前插件未支持此类型数据库");
    	}
    }

    /**
     * TODO 需要使用其他数据库需要改写
     * 分页获取参数的SQL
     * 这里需要根据数据库的类型改写SQL,目前支持MySQL和Oracle
     * @param currSql —— 当前执行的SQL
     * @return 改写后的SQL
     * @throws NotSupportedException
     */
    private String getPageDataSQL(String currSql, String dbType) throws NotSupportedException {
    	if (DB_TYPE_MYSQL.equals(dbType)) {
    	    return "select * from (" + currSql + ") $_paging_table limit ?, ?";
    	} else if (DB_TYPE_ORACLE.equals(dbType)) {
    		return " select * from (select cur_sql_result.*, rownum rn from (" + currSql + ") cur_sql_result  where rownum <= ?) where rn > ?";
    	} else {
    		throw new NotSupportedException("当前插件未支持此类型数据库");
    	}
    }

    /**
     * TODO 需要使用其他数据库需要改写
     * 使用PreparedStatement预编译两个分页参数,如果数据库的规则不一样,需要改写设置的参数规则。目前支持MySQL和Oracle
     * @throws SQLException
     * @throws NotSupportedException
     *
     */
    private void preparePageDataParams(PreparedStatement ps, int pageNum, int pageSize, String dbType) throws Exception {
    	//prepared()方法编译SQL,由于MyBatis上下文没有我们分页参数的信息,所以这里需要设置这两个参数.
        	//获取需要设置的参数个数,由于我们的参数是最后的两个,所以很容易得到其位置
        	int idx = ps.getParameterMetaData().getParameterCount();
        	if (DB_TYPE_MYSQL.equals(dbType)) {
	        	//最后两个是我们的分页参数.
	        	ps.setInt(idx -1, (pageNum - 1) * pageSize);//开始行
	        	ps.setInt(idx, pageSize); //限制条数
        	} else if (DB_TYPE_ORACLE.equals(dbType)) {
        		ps.setInt(idx -1, pageNum * pageSize);//结束行
        		ps.setInt(idx, (pageNum - 1) * pageSize); //开始行
        	} else {
        		throw new NotSupportedException("当前插件未支持此类型数据库");
        	}

    }

    /**
     *
     * TODO 需要使用其他数据库需要改写
     * 目前支持MySQL和Oracle
     * @param mappedStatement
     * @return
     * @throws Exception
     */
    private String getDataSourceType(MappedStatement mappedStatement) throws Exception {
    	String dbConnectionStr = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection().toString();
    	dbConnectionStr = dbConnectionStr.toLowerCase();
        if (dbConnectionStr.contains(DB_TYPE_MYSQL)) {
        	return DB_TYPE_MYSQL;
        } else if (dbConnectionStr.contains(DB_TYPE_ORACLE)) {
        	return DB_TYPE_ORACLE;
        } else {
        	throw new NotSupportedException("当前插件未支持此类型数据库");
        }
    }

}



如果你是使用非Oracle或者MySQL的,你需要自己注解有TODO的方法,修改是也很简单。

有了这个插件,还要配置一下,MyBatis的配置文件如下:

<configuration>
......
	<plugins>
		<plugin interceptor="com.learn.mybatis.plugin.PagingPlugin">
		    <!-- 默认页码 -->
			<property name="default.page" value="1" />
			<!-- 默认每页条数-->
			<property name="default.pageSize" value="20" />
			<!-- 是否启动分页插件功能  -->
			<property name="default.useFlag" value="true" />
			<!-- 是否检查页码有效性,如果非有效,则抛出异常. -->
			<property name="default.checkFlag" value="false" />
			<!-- 针对哪些含有order by的SQL,是否去掉最后一个order by 以后的SQL语句,提高性能 -->
			<property name="default.cleanOrderBy" value="false" />
		</plugin>
	</plugins>
......
</configuration>

上面我们可以看到一些默认值,可以根据我自己定义的一个POJO修改上面配置的默认值,这个POJO十分简单,它符合JavaBean的一切规范,如下:(其属性含义见其注解)

package com.learn.mybatis.plugin;

import java.util.List;

/**
 *
 * @author ykzhen2015
 */
public class PageParams {

	private Integer page;//第几页
	private Integer pageSize;//每页限制条数
	private Boolean useFlag;//是否启动插件,如果不启动,则不作分页
	private Boolean checkFlag;//是否检测页码的有效性,如果为true,而页码大于最大页数,则抛出异常
	private Boolean cleanOrderBy;//是否清除最后order by 后面的语句
	private Integer total; //总条数,插件会回填这个值
	private Integer totalPage; //总页数,插件会回填这个值.

	public Integer getPage() {
		return page;
	}

	public void setPage(Integer page) {
		this.page = page;
	}

	public Integer getPageSize() {
		return pageSize;
	}

	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}

	public Boolean getUseFlag() {
		return useFlag;
	}

	public void setUseFlag(Boolean useFlag) {
		this.useFlag = useFlag;
	}

	public Boolean getCheckFlag() {
		return checkFlag;
	}

	public void setCheckFlag(Boolean checkFlag) {
		this.checkFlag = checkFlag;
	}

	public Boolean getCleanOrderBy() {
		return cleanOrderBy;
	}

	public void setCleanOrderBy(Boolean cleanOrderBy) {
		this.cleanOrderBy = cleanOrderBy;
	}

	public Integer getTotal() {
		return total;
	}

	public void setTotal(Integer total) {
		this.total = total;
	}

	public Integer getTotalPage() {
		return totalPage;
	}

	public void setTotalPage(Integer totalPage) {
		this.totalPage = totalPage;
	}

}

当以下属性被设置会有这样的效果:

  • page  --插件默认设置default.page作废,采取这个值,整数型
  • pageSize --插件默认设置default.pageSize作废,采取这个值,整数型
  • useFlag --插件默认设置default.useFlag作废,采取这个值,布尔值
  • checkFlag --插件默认设置default.checkFlag作废,采取这个值,布尔值
  • cleanOrderBy -- 插件默认设置default.cleanOrderBy作废,采用这个值,布尔值

而:

total  ——代表总条数,插件回自动帮我们回填,整数型

totalPage ——代表总页数,插件回自动帮我们回填,整数型


3、插件的使用:

这个插件使用起来十分简单,只要传递给MyBatis的SqlMap以下的方式都是允许的:

  • 继承com.learn.mybatis.plugin.PageParams
  • 使用map,但是map中的键值对应,有一个值是com.learn.mybatis.plugin.PageParams类型
  • 使用@Param注解,只要里面有一个参数是com.learn.mybatis.plugin.PageParams类型
  • 使用POJO,但是这个POJO中有一个属性为com.learn.mybatis.plugin.PageParams类型

以上4种形式都可以使用这个分页插件,是不是很灵活易用??

让我们试用一下这个插件,看看这四种传递参数的方法。


现在我们定义一个map.xml如下:

<?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learn.mybatis.chapter1.mapper.RoleMapper">
    <select id="getRole" parameterType="long" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role where id=#{id}
    </select>
    <select id="selectAllRole" parameterType="com.learn.mybatis.plugin.TestParams" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role
        <where>
        <if test="roleName != null">
        role_name like concat('%', #{roleName}, '%')
        </if>

        </where>
        order by id desc
    </select>


    <select id="selectAllRole2" parameterType="com.learn.mybatis.plugin.TestParams2" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role
        <where>
        <if test="roleName != null">
        role_name like concat('%', #{roleName}, '%')
        </if>
        </where>
        order by id desc
    </select>

    <select id="selectRoleByMap" parameterType="map" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role
        <where>
        <if test="roleName != null">
        role_name like concat('%', #{roleName}, '%')
        </if>
        </where>
        order by id desc
    </select>

    <select id="selectRoleByMap2" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role
    </select>

    <select id="selectRoleByKey"  resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from t_role
        <where>
        <if test="roleName != null">
        role_name like concat('%', #{roleName}, '%')
        </if>
        </where>
        order by id desc
    </select>

    <select id="test$sql" resultType="com.learn.mybatis.chapter1.po.TRole">
        select id, role_name as roleName, note from ${tableName}
        <where>
        <if test="roleName != null">
        role_name like concat('%', #{roleName}, '%')
        </if>
        </where>
        order by role_name desc
    </select>
</mapper>

定义其接口:

package com.learn.mybatis.chapter1.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;

import com.learn.mybatis.chapter1.po.TRole;
import com.learn.mybatis.plugin.PageParams;
import com.learn.mybatis.plugin.TestParams;
import com.learn.mybatis.plugin.TestParams2;

public interface RoleMapper {

	//不含分页参数,就不会启用
	public TRole getRole(Long id);

	//继承方式
	public List<TRole> selectAllRole(TestParams pageParams);

	//参数为POJO,但是POJO内部包含一个类型为com.learn.mybatis.plugin.PageParams的属性
	public List<TRole> selectAllRole2(TestParams2 testParams2);

	//使用map,里面有一个com.learn.mybatis.plugin.PageParams类型的值
	public List<TRole> selectRoleByMap(Map<String, Object> params);

	//使用注解的形式传递分页参数。
	public List<TRole> selectRoleByMap2(@Param("roleName") String roleName, @Param("page221232323")  PageParams pageParams);



}


好,看到注解,里面已经包含了上述4种传参的方法,这个插件都是支持的。先看到selectAllRole()方法

里面的参数TestParams定义(继承PageParams):

package com.learn.mybatis.plugin;

public class TestParams extends PageParams {

	String roleName;

	public String getRoleName() {
		return roleName;
	}

	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}
}
再看到selectAllRole2()方法,里面的参数TestParams2定义(其属性有一个PageParams):

package com.learn.mybatis.plugin;

public class TestParams2 {

	private PageParams pageParams = null;
	private String roleName;

	public PageParams getPageParams() {
		return pageParams;
	}

	public void setPageParams(PageParams pageParams) {
		this.pageParams = pageParams;
	}

	public String getRoleName() {
		return roleName;
	}

	public void setRoleName(String roleName) {
		this.roleName = roleName;
	}

}

好其他的都很简单了。

我们来测试这个接口,测试代码如下,注意看四种方式传递参数,代码的污染是很小的:

package com.learn.mybatis.chapter1.main;

import java.util.HashMap;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

import com.learn.mybatis.chapter1.mapper.RoleMapper;
import com.learn.mybatis.chapter1.mb.util.MyBatisUtil;
import com.learn.mybatis.plugin.PageParams;
import com.learn.mybatis.plugin.TestParams;
import com.learn.mybatis.plugin.TestParams2;

public class MyBatisExample {

	private static Logger logger = Logger.getLogger(MyBatisExample.class);
	public static void main(String[] args) {
		testGetRole();
		testInherit();
		testMap();
		testAtParam();
		testPojoProperty();

	}

	/**
	 * 继承PageParams的参数传递.
	 */
	public static void testGetRole() {
		logger.info("#################测试没有分页参数的查询###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			roleMapper.getRole(1L);
		} finally {
			sqlSession.close();
		}
	}

	/**
	 * 继承PageParams的参数传递.
	 */
	public static void testInherit() {
		logger.info("#################测试分页参数以继承PageParams形式传递###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			TestParams params = new TestParams();
			params.setUseFlag(true);
			params.setCheckFlag(false);
			params.setPage(2);
			params.setPageSize(5);
			params.setRoleName("test");
			roleMapper.selectAllRole(params);
			logger.info("总条数==>" + params.getTotal());
			logger.info("总页数 ==>" + params.getTotalPage());
		} finally {
			sqlSession.close();
		}
	}

	/**
	 * 使用map方式。(map的键随意取)
	 */
	public static void testMap() {
		logger.info("#################测试分页参数以Map形式传递###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			Map<String, Object> paramMap = new HashMap<String, Object>();
			PageParams pageParams = new PageParams();
			pageParams.setUseFlag(true);
			pageParams.setCheckFlag(false);
			pageParams.setPage(2);
			pageParams.setPageSize(5);
			paramMap.put("roleName", "test");
			paramMap.put("page_drsdsd2233", pageParams);
			roleMapper.selectRoleByMap(paramMap);
			logger.info("总条数==>" + pageParams.getTotal());
			logger.info("总页数 ==>" + pageParams.getTotalPage());
		} finally {
			sqlSession.close();
		}
	}

	/**
	 * 使用MyBatis  @Param注解 传递参数.
	 */
	public static void testAtParam() {
		logger.info("#################测试分页参数以@Param形式传递###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			PageParams pageParams2 = new PageParams();
			pageParams2.setUseFlag(true);
			pageParams2.setCheckFlag(true);
			pageParams2.setPage(2);
			pageParams2.setPageSize(5);
			pageParams2.setCleanOrderBy(true);
			roleMapper.selectRoleByMap2("test", pageParams2);
			logger.info("总条数==>" + pageParams2.getTotal());
			logger.info("总页数 ==>" + pageParams2.getTotalPage());
		} finally {
			sqlSession.close();
		}
	}

	/**
	 * 使用POJO传递,但是POJO内部有一个PageParams属性,和它的setter方法.
	 */
	public static void testPojoProperty() {
		logger.info("#################测试分页参数作为参数属性传递###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			PageParams pageParams3 = new PageParams();
			pageParams3.setUseFlag(true);
			pageParams3.setCheckFlag(false);
			pageParams3.setPage(3);
			pageParams3.setPageSize(5);
			pageParams3.setCleanOrderBy(true);
			TestParams2 p2 = new TestParams2();
			p2.setPageParams(pageParams3);
			p2.setRoleName("test");
			roleMapper.selectAllRole2(p2);
			logger.info("总条数==>" + pageParams3.getTotal());
			logger.info("总页数 ==>" + pageParams3.getTotalPage());
		} finally {
			sqlSession.close();
		}
	}
}

测试的结果如下(我这里使用mysql也可以使用oracle,我也有测试过的):

 INFO 2016-06-23 21:02:37,966 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试没有分页参数的查询###########################
DEBUG 2016-06-23 21:02:38,169 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:02:38,186 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:02:38,267 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:02:38,470 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 103887628.
DEBUG 2016-06-23 21:02:38,470 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,485 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1752203484.
DEBUG 2016-06-23 21:02:38,486 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select id, role_name as roleName, note from t_role where id=? 
DEBUG 2016-06-23 21:02:38,508 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2016-06-23 21:02:38,532 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 0
DEBUG 2016-06-23 21:02:38,533 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,534 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,535 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.
 INFO 2016-06-23 21:02:38,539 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以继承PageParams形式传递###########################
DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool.
DEBUG 2016-06-23 21:02:38,565 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,575 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1682463303.
DEBUG 2016-06-23 21:02:38,576 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging 
DEBUG 2016-06-23 21:02:38,576 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String)
DEBUG 2016-06-23 21:02:38,577 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-06-23 21:02:38,577 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? 
DEBUG 2016-06-23 21:02:38,579 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer), test(String)
DEBUG 2016-06-23 21:02:38,583 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 5
 INFO 2016-06-23 21:02:38,584 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10
 INFO 2016-06-23 21:02:38,584 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2
DEBUG 2016-06-23 21:02:38,584 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,585 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,585 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.
 INFO 2016-06-23 21:02:38,585 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以Map形式传递###########################
DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool.
DEBUG 2016-06-23 21:02:38,588 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,601 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 156545103.
DEBUG 2016-06-23 21:02:38,601 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging 
DEBUG 2016-06-23 21:02:38,602 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String)
DEBUG 2016-06-23 21:02:38,604 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-06-23 21:02:38,604 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? 
DEBUG 2016-06-23 21:02:38,605 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer), test(String)
DEBUG 2016-06-23 21:02:38,608 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 5
 INFO 2016-06-23 21:02:38,609 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10
 INFO 2016-06-23 21:02:38,609 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2
DEBUG 2016-06-23 21:02:38,609 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,610 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,610 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.
 INFO 2016-06-23 21:02:38,610 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数以@Param形式传递###########################
DEBUG 2016-06-23 21:02:38,612 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:02:38,613 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool.
DEBUG 2016-06-23 21:02:38,613 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,627 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 668210649.
DEBUG 2016-06-23 21:02:38,627 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role) $_paging 
DEBUG 2016-06-23 21:02:38,628 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 
DEBUG 2016-06-23 21:02:38,630 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-06-23 21:02:38,630 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from (select id, role_name as roleName, note from t_role) $_paging_table limit ?, ? 
DEBUG 2016-06-23 21:02:38,631 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 5(Integer), 5(Integer)
DEBUG 2016-06-23 21:02:38,633 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 5
 INFO 2016-06-23 21:02:38,634 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10
 INFO 2016-06-23 21:02:38,634 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2
DEBUG 2016-06-23 21:02:38,634 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,635 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,635 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.
 INFO 2016-06-23 21:02:38,635 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数作为参数属性传递###########################
DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.datasource.pooled.PooledDataSource: Checked out connection 103887628 from pool.
DEBUG 2016-06-23 21:02:38,636 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,643 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 1361289747.
DEBUG 2016-06-23 21:02:38,644 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') ) $_paging 
DEBUG 2016-06-23 21:02:38,644 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String)
DEBUG 2016-06-23 21:02:38,645 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-06-23 21:02:38,645 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select * from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging_table limit ?, ? 
DEBUG 2016-06-23 21:02:38,646 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 10(Integer), 5(Integer), test(String)
DEBUG 2016-06-23 21:02:38,648 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 0
 INFO 2016-06-23 21:02:38,648 com.learn.mybatis.chapter1.main.MyBatisExample: 总条数==>10
 INFO 2016-06-23 21:02:38,648 com.learn.mybatis.chapter1.main.MyBatisExample: 总页数 ==>2
DEBUG 2016-06-23 21:02:38,648 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,649 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:02:38,649 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 103887628 to pool.


此时为了测试验证页码的和去掉order by语句的效果,我们再把testPojoProperty()方法修改一下:

/**
	 * 使用POJO传递,但是POJO内部有一个PageParams属性,和它的setter方法.
	 */
	public static void testPojoProperty() {
		logger.info("#################测试分页参数作为参数属性传递###########################");
		SqlSession sqlSession = null;
		try {
			sqlSession = MyBatisUtil.getSqlSessionFactroy().openSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			PageParams pageParams3 = new PageParams();
			pageParams3.setUseFlag(true);
			pageParams3.setCheckFlag(true);//检验页码的有效性。
			pageParams3.setPage(6);//超过最大页数
			pageParams3.setPageSize(5);
			pageParams3.setCleanOrderBy(false);//不去掉order by 语句
			TestParams2 p2 = new TestParams2();
			p2.setPageParams(pageParams3);
			p2.setRoleName("test");
			roleMapper.selectAllRole2(p2);
			logger.info("总条数==>" + pageParams3.getTotal());
			logger.info("总页数 ==>" + pageParams3.getTotalPage());
		} finally {
			sqlSession.close();
		}
	}

再运行一下,得到日志:

  INFO 2016-06-23 21:09:09,643 com.learn.mybatis.chapter1.main.MyBatisExample: #################测试分页参数作为参数属性传递###########################
DEBUG 2016-06-23 21:09:09,839 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:09:09,856 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:09:09,857 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2016-06-23 21:09:09,957 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Opening JDBC Connection
DEBUG 2016-06-23 21:09:10,166 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 142555199.
DEBUG 2016-06-23 21:09:10,166 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:09:10,181 org.apache.ibatis.datasource.pooled.PooledDataSource: Created connection 192794887.
DEBUG 2016-06-23 21:09:10,182 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select count(*) as total from (select id, role_name as roleName, note from t_role WHERE role_name like concat('%', ?, '%') order by id desc) $_paging 
DEBUG 2016-06-23 21:09:10,218 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: test(String)
DEBUG 2016-06-23 21:09:10,243 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-06-23 21:09:10,245 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Resetting autocommit to true on JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:09:10,245 org.apache.ibatis.transaction.jdbc.JdbcTransaction: Closing JDBC Connection [[email protected]]
DEBUG 2016-06-23 21:09:10,246 org.apache.ibatis.datasource.pooled.PooledDataSource: Returned connection 142555199 to pool.
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: java.lang.reflect.UndeclaredThrowableException
### The error may exist in com\learn\mybatis\chapter1\sqlmap\role.xml
### The error may involve com.learn.mybatis.chapter1.mapper.RoleMapper.selectAllRole2-Inline
### The error occurred while setting parameters
### Cause: java.lang.reflect.UndeclaredThrowableException
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:111)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119)
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63)
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
    at com.sun.proxy.$Proxy4.selectAllRole2(Unknown Source)
    at com.learn.mybatis.chapter1.main.MyBatisExample.testPojoProperty(MyBatisExample.java:131)
    at com.learn.mybatis.chapter1.main.MyBatisExample.main(MyBatisExample.java:23)
Caused by: java.lang.reflect.UndeclaredThrowableException
    at com.sun.proxy.$Proxy5.prepare(Unknown Source)
    at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:73)
    at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:59)
    at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267)
    at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96)
    at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108)
    ... 7 more
Caused by: java.lang.Exception: 查询失败,查询页码【6】大于总总页【2】!!
    at com.learn.mybatis.plugin.PagingPlugin.checkPage(PagingPlugin.java:168)
    at com.learn.mybatis.plugin.PagingPlugin.intercept(PagingPlugin.java:99)
    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:60)
    ... 15 more




说明页码超过了最大的页数,注意这里的因为有了这句:pageParams3.setCleanOrderBy(false);所以从日志可以看出,和上次不太一样,插件没有帮我们去掉了影响性能的order by语句。这些都是可以通过参数由你来控制的。当然使用默认的参数也是可以的。








以上是关于MyBatis分页插件:比PageHelper都要简单,使用很灵活,目前支持Oracle和MySQL的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis插件原理和PageHelper结合实战分页插件

Mybatis分页插件pageHelper

mybatis分页插件PageHelper的使用(转)

基于Mybatis分页插件PageHelper

mybatis分页插件pagehelper

Mybatis 的分页插件PageHelper-4.1.1的使用