Mybatis 数据库物理分页插件 PageHelper

以前使用 ibatis/mybatis,都是自己手写 sql 语句进行物理分页,虽然稍微有点麻烦,但是都习惯了。最近试用了下 mybatis 的分页插件 PageHelper, 感觉还不错吧。记录下其使用方法。

1. 引入依赖 jar 包:

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>3.7.5</version>
    </dependency>

2. 配置分页拦截器

PageHelper 的原理是基于拦截器实现的。拦截器的配置有两种方法,一种是在 mybatis 的配置文件中配置,一种是直接在 spring 的配置文件中进行:

1)在 mybatis-config.xml 文件中配置:

  <plugins>
    <!-- com.github.pagehelper 为 PageHelper 类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <!-- 该参数默认为 false -->
        <!-- 设置为 true 时,会将 RowBounds 第一个参数 offset 当成 pageNum 页码使用 -->
        <!-- 和 startPage 中的 pageNum 效果一样-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 该参数默认为 false -->
        <!-- 设置为 true 时,使用 RowBounds 分页会进行 count 查询 -->
        <property name="rowBoundsWithCount" value="true"/>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> (相当于没有执行分页查询,但是返回结果仍然是Page类型)
    &lt;property name="pageSizeZero" value="true"/&gt;</span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 3.3.0版本可用 - 分页参数合理化,默认false禁用 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 启用合理化时,如果pageNum&lt;1会查询第一页,如果pageNum&gt;pages会查询最后一页 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 禁用合理化时,如果pageNum&lt;1或pageNum&gt;pages会返回空数据 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="reasonable"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="true"</span><span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 3.5.0版本可用 - 为了支持startPage(Object params)方法 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> 不理解该含义的前提下,不要随便复制该配置 
    &lt;property name="params" value="pageNum=start;pageSize=limit;"/&gt;    </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">plugin</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

</plugins>

这里要注意 <plugins> 在 mybatis-config.xml 文件中的位置,必须要符合 http://mybatis.org/dtd/mybatis-3-config.dtd 中指定的顺序:

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, 
objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)
>

不然会报错。

当然 mybatis-config.xml 的位置,我们需要在 spring 的配置文件中进行指定:

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="configLocation" value="classpath:config/mybatis-config.xml" />
      <property name="mapperLocations" value="classpath*:config/mappers/**/*.xml" />
    </bean>

2)如果 mybatis 没有 mybatis-config.xml 文件,那么就只能直接在 spring 的配置文件中配置了:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="mapperLocations">
    <array>
      <value>classpath:config/mapper/*.xml</value>
    </array>
  </property>
  <property name="typeAliasesPackage" value="com.test.pojo"/>
  <property name="plugins">
    <array>
      <bean class="com.github.pagehelper.PageHelper">
        <property name="properties">
          <value>
            dialect=mysql
          </value>
        </property>
      </bean>
    </array>
  </property>
</bean>

到这里 PageHelper 所需要的配置已经完成,下面还需要在 serviceImpl 类中加入分页参数的代码:

3. 向拦截器传递分页参数

我们首先看下不分页的 serviceImpl 代码:

    @Override
    public List<User> getUserByNoAndEmail(String no, String email) {
        Map<String, Object> map = new HashMap<>();
        map.put("no", no);
        map.put("email", email);
        return this.userMapper.getUserByNoAndEmail(map);
    }

然后我们将它改造成使用 PageHelper 分页:

1)首先我们根据自己项目的情况,定义一个 PageBean,来保存分页之后的结果,需要哪些属性,就加入哪些属性,具体可以参考源代码中的 PageInfo 类的定义,其实PageInfo 是插件作者给我们自己定义自己的 PageBean,提供的一个参考例子。PageInfo 代码如下:

@SuppressWarnings({"rawtypes", "unchecked"})
public class PageInfo<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    //当前页
    private int pageNum;
    //每页的数量
    private int pageSize;
    //当前页的数量
    private int size;
    //由于 startRow 和 endRow 不常用,这里说个具体的用法
    //可以在页面中 "显示 startRow 到 endRow 共 size 条数据"
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前页面第一个元素在数据库中的行号</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> startRow;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当前页面最后一个元素在数据库中的行号</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> endRow;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">总记录数</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> total;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">总页数</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pages;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">结果集</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> list;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">第一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> firstPage;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">前一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> prePage;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">下一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> nextPage;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最后一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> lastPage;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否为第一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> isFirstPage = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否为最后一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> isLastPage = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否有前一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> hasPreviousPage = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">是否有下一页</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> hasNextPage = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">导航页码数</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> navigatePages;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">所有导航页号</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">[] navigatepageNums;

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 包装Page对象
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> list
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> PageInfo(List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> list) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>(list, 8<span style="color: rgba(0, 0, 0, 1)">);
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 包装Page对象
 *
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> list          page结果
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> navigatePages 页码数量
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> PageInfo(List&lt;T&gt; list, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> navigatePages) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (list <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Page) {
        Page page </span>=<span style="color: rgba(0, 0, 0, 1)"> (Page) list;
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageNum =<span style="color: rgba(0, 0, 0, 1)"> page.getPageNum();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageSize =<span style="color: rgba(0, 0, 0, 1)"> page.getPageSize();

        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.total =<span style="color: rgba(0, 0, 0, 1)"> page.getTotal();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pages =<span style="color: rgba(0, 0, 0, 1)"> page.getPages();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.list =<span style="color: rgba(0, 0, 0, 1)"> page;
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.size =<span style="color: rgba(0, 0, 0, 1)"> page.size();
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">由于结果是&gt;startRow的,所以实际的需要+1</span>
        <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.size == 0<span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.startRow = 0<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.endRow = 0<span style="color: rgba(0, 0, 0, 1)">;
        } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.startRow = page.getStartRow() + 1<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">计算实际的endRow(最后一页的时候特殊)</span>
            <span style="color: rgba(0, 0, 255, 1)">this</span>.endRow = <span style="color: rgba(0, 0, 255, 1)">this</span>.startRow - 1 + <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.size;
        }
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.navigatePages =<span style="color: rgba(0, 0, 0, 1)"> navigatePages;
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">计算导航页</span>

calcNavigatepageNums();
//计算前后页,第一页,最后一页
calcPage();
//判断页面边界
judgePageBoudary();
}
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 计算导航页
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> calcNavigatepageNums() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当总页数小于或等于导航页码数时</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (pages &lt;=<span style="color: rgba(0, 0, 0, 1)"> navigatePages) {
        navigatepageNums </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">[pages];
        </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; pages; i++<span style="color: rgba(0, 0, 0, 1)">) {
            navigatepageNums[i] </span>= i + 1<span style="color: rgba(0, 0, 0, 1)">;
        }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span> { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">当总页数大于导航页码数时</span>
        navigatepageNums = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">[navigatePages];
        </span><span style="color: rgba(0, 0, 255, 1)">int</span> startNum = pageNum - navigatePages / 2<span style="color: rgba(0, 0, 0, 1)">;
        </span><span style="color: rgba(0, 0, 255, 1)">int</span> endNum = pageNum + navigatePages / 2<span style="color: rgba(0, 0, 0, 1)">;

        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (startNum &lt; 1<span style="color: rgba(0, 0, 0, 1)">) {
            startNum </span>= 1<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">(最前navigatePages页</span>
            <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; navigatePages; i++<span style="color: rgba(0, 0, 0, 1)">) {
                navigatepageNums[i] </span>= startNum++<span style="color: rgba(0, 0, 0, 1)">;
            }
        } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (endNum &gt;<span style="color: rgba(0, 0, 0, 1)"> pages) {
            endNum </span>=<span style="color: rgba(0, 0, 0, 1)"> pages;
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">最后navigatePages页</span>
            <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = navigatePages - 1; i &gt;= 0; i--<span style="color: rgba(0, 0, 0, 1)">) {
                navigatepageNums[i] </span>= endNum--<span style="color: rgba(0, 0, 0, 1)">;
            }
        } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">所有中间页</span>
            <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; navigatePages; i++<span style="color: rgba(0, 0, 0, 1)">) {
                navigatepageNums[i] </span>= startNum++<span style="color: rgba(0, 0, 0, 1)">;
            }
        }
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 计算前后页,第一页,最后一页
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> calcPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (navigatepageNums != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; navigatepageNums.length &gt; 0<span style="color: rgba(0, 0, 0, 1)">) {
        firstPage </span>= navigatepageNums[0<span style="color: rgba(0, 0, 0, 1)">];
        lastPage </span>= navigatepageNums[navigatepageNums.length - 1<span style="color: rgba(0, 0, 0, 1)">];
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pageNum &gt; 1<span style="color: rgba(0, 0, 0, 1)">) {
            prePage </span>= pageNum - 1<span style="color: rgba(0, 0, 0, 1)">;
        }
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pageNum &lt;<span style="color: rgba(0, 0, 0, 1)"> pages) {
            nextPage </span>= pageNum + 1<span style="color: rgba(0, 0, 0, 1)">;
        }
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 判定页面边界
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> judgePageBoudary() {
    isFirstPage </span>= pageNum == 1<span style="color: rgba(0, 0, 0, 1)">;
    isLastPage </span>= pageNum ==<span style="color: rgba(0, 0, 0, 1)"> pages;
    hasPreviousPage </span>= pageNum &gt; 1<span style="color: rgba(0, 0, 0, 1)">;
    hasNextPage </span>= pageNum &lt;<span style="color: rgba(0, 0, 0, 1)"> pages;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPageNum(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pageNum) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageNum =<span style="color: rgba(0, 0, 0, 1)"> pageNum;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageNum() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageNum;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageSize() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageSize;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getSize() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> size;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getStartRow() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> startRow;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getEndRow() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> endRow;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> getTotal() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> total;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPages() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pages;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> getList() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> list;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getFirstPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> firstPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPrePage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> prePage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getNextPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> nextPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getLastPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> lastPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isIsFirstPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> isFirstPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isIsLastPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> isLastPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isHasPreviousPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> hasPreviousPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> isHasNextPage() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> hasNextPage;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getNavigatePages() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> navigatePages;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">[] getNavigatepageNums() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> navigatepageNums;
}

@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String toString() {
    </span><span style="color: rgba(0, 0, 255, 1)">final</span> StringBuffer sb = <span style="color: rgba(0, 0, 255, 1)">new</span> StringBuffer("PageInfo{"<span style="color: rgba(0, 0, 0, 1)">);
    sb.append(</span>"pageNum="<span style="color: rgba(0, 0, 0, 1)">).append(pageNum);
    sb.append(</span>", pageSize="<span style="color: rgba(0, 0, 0, 1)">).append(pageSize);
    sb.append(</span>", size="<span style="color: rgba(0, 0, 0, 1)">).append(size);
    sb.append(</span>", startRow="<span style="color: rgba(0, 0, 0, 1)">).append(startRow);
    sb.append(</span>", endRow="<span style="color: rgba(0, 0, 0, 1)">).append(endRow);
    sb.append(</span>", total="<span style="color: rgba(0, 0, 0, 1)">).append(total);
    sb.append(</span>", pages="<span style="color: rgba(0, 0, 0, 1)">).append(pages);
    sb.append(</span>", list="<span style="color: rgba(0, 0, 0, 1)">).append(list);
    sb.append(</span>", firstPage="<span style="color: rgba(0, 0, 0, 1)">).append(firstPage);
    sb.append(</span>", prePage="<span style="color: rgba(0, 0, 0, 1)">).append(prePage);
    sb.append(</span>", nextPage="<span style="color: rgba(0, 0, 0, 1)">).append(nextPage);
    sb.append(</span>", lastPage="<span style="color: rgba(0, 0, 0, 1)">).append(lastPage);
    sb.append(</span>", isFirstPage="<span style="color: rgba(0, 0, 0, 1)">).append(isFirstPage);
    sb.append(</span>", isLastPage="<span style="color: rgba(0, 0, 0, 1)">).append(isLastPage);
    sb.append(</span>", hasPreviousPage="<span style="color: rgba(0, 0, 0, 1)">).append(hasPreviousPage);
    sb.append(</span>", hasNextPage="<span style="color: rgba(0, 0, 0, 1)">).append(hasNextPage);
    sb.append(</span>", navigatePages="<span style="color: rgba(0, 0, 0, 1)">).append(navigatePages);
    sb.append(</span>", navigatepageNums="<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (navigatepageNums == <span style="color: rgba(0, 0, 255, 1)">null</span>) sb.append("null"<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
        sb.append(</span>'['<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; navigatepageNums.length; ++<span style="color: rgba(0, 0, 0, 1)">i)
            sb.append(i </span>== 0 ? "" : ", "<span style="color: rgba(0, 0, 0, 1)">).append(navigatepageNums[i]);
        sb.append(</span>']'<span style="color: rgba(0, 0, 0, 1)">);
    }
    sb.append(</span>'}'<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> sb.toString();
}

}

PageInfo.java

因为 PageInfo.java 只是一个示例,所以他定义得有点重量级,属性有点多,我们可以参考它,定义适合我们自己的 PageBean, 比如如下定义:

public class PageBean<T> implements Serializable {
    private static final long serialVersionUID = 8656597559014685635L;
    private long total;        //总记录数
    private List<T> list;    //结果集
    private int pageNum;    // 第几页
    private int pageSize;    // 每页记录数
    private int pages;        // 总页数
    private int size;        // 当前页的数量 <= pageSize,该属性来自 ArrayList 的 size 属性
<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 包装Page对象,因为直接返回Page对象,在JSON处理以及其他情况下会被当成List来处理,
 * 而出现一些问题。
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> list          page结果
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> navigatePages 页码数量
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> PageBean(List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> list) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (list <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Page) {
        Page</span>&lt;T&gt; page = (Page&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)">) list;
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageNum =<span style="color: rgba(0, 0, 0, 1)"> page.getPageNum();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageSize =<span style="color: rgba(0, 0, 0, 1)"> page.getPageSize();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.total =<span style="color: rgba(0, 0, 0, 1)"> page.getTotal();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pages =<span style="color: rgba(0, 0, 0, 1)"> page.getPages();
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.list =<span style="color: rgba(0, 0, 0, 1)"> page;
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.size =<span style="color: rgba(0, 0, 0, 1)"> page.size();
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> getTotal() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> total;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setTotal(<span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> total) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.total =<span style="color: rgba(0, 0, 0, 1)"> total;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> getList() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> list;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setList(List&lt;T&gt;<span style="color: rgba(0, 0, 0, 1)"> list) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.list =<span style="color: rgba(0, 0, 0, 1)"> list;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageNum() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageNum;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPageNum(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pageNum) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageNum =<span style="color: rgba(0, 0, 0, 1)"> pageNum;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageSize() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pageSize;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPageSize(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pageSize) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pageSize =<span style="color: rgba(0, 0, 0, 1)"> pageSize;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPages() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pages;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPages(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pages) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pages =<span style="color: rgba(0, 0, 0, 1)"> pages;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getSize() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> size;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setSize(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> size) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.size =<span style="color: rgba(0, 0, 0, 1)"> size;
}

}

PageBean.java

因为分页查询结果返回的是一个 Page 对象,而 Page 对象继承自 ArrayList,但是如果我们直接返回 ArrayList 的话,在一些场景下回遇到问题,比如在 JSON 处理 Page 类型的结果时,会被当成 List 来 JSON 格式化,会丢弃 Page 对象的所有扩展属性,所以这里我们要将分页的结果 Page 类型转换成我们自己定义的 PageBean. 我们自己定义的 PageBean 没有继承 ArrayList,而是包含一个 List 属性来保存分页结果。所以避免前面的问题。

2)修改 serviceImpl 中的代码:

    @Override
    public PageBean<User> getUserByNoAndEmail(String no, String email) {
        Map<String, Object> map = new HashMap<>();
        map.put("no", no);
        map.put("email", email);
    PageHelper.startPage(PaginationContext.getPageNum(), PaginationContext.getPageSize());
    List</span>&lt;User&gt; list = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.userMapper.getUserByNoAndEmail(map);
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> PageBean&lt;User&gt;<span style="color: rgba(0, 0, 0, 1)">(list);
}</span></pre>

我们只需要使用 PageHelper.startPage(pageNum, pageSize); 函数来指定 pageNum(第几页) 和 pageSize(每页显示几条记录) 两个参数。然后调用原来的查询,就进行了分页。最后将返回的 List,转换成 PageBean 类型的结果即可。前台页面就可以根据 PageBean 中包括的属性来进行分页显示了。

上面的 PaginationContext 是基于 ThreadLocal 来传递分页参数的一个工具类,其实现如下:

public class PaginationContext {
    // 定义两个 threadLocal 变量:pageNum 和 pageSize
    private static ThreadLocal<Integer> pageNum = new ThreadLocal<Integer>();    // 保存第几页
    private static ThreadLocal<Integer> pageSize = new ThreadLocal<Integer>();    // 保存每页记录条数
<span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
 * pageNum :get、set、remove
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageNum() {
    Integer pn </span>=<span style="color: rgba(0, 0, 0, 1)"> pageNum.get();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pn == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> 0<span style="color: rgba(0, 0, 0, 1)">;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> pn;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPageNum(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pageNumValue) {
    pageNum.set(pageNumValue);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> removePageNum() {
    pageNum.remove();
}

</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
 * pageSize :get、set、remove
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getPageSize() {
    Integer ps </span>=<span style="color: rgba(0, 0, 0, 1)"> pageSize.get();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (ps == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> 0<span style="color: rgba(0, 0, 0, 1)">;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ps;
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setPageSize(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> pageSizeValue) {
    pageSize.set(pageSizeValue);
}

</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> removePageSize() {
    pageSize.remove();
}

}

PaginationContext.java

实现了前台页面向 ServiceImpl 中传递分页参数: pageNum 和 pageSize.

当然从请求中获取分页参数 pageNum 和 pageSize 需要用到 filter:

public class PageFilter implements Filter {public PageFilter() {}
public void destroy() {}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;

    PaginationContext.setPageNum(getPageNum(httpRequest));
    PaginationContext.setPageSize(getPageSize(httpRequest));

    try {
        chain.doFilter(request, response);
    }
    // 使用完Threadlocal,将其删除。使用finally确保一定将其删除
    finally {
        PaginationContext.removePageNum();
        PaginationContext.removePageSize();
    }
}

/**
 * 获得pager.offset参数的值
 * 
 * @param request
 * @return
 */
protected int getPageNum(HttpServletRequest request) {
    int pageNum = 1;
    try {
        String pageNums = request.getParameter("pageNum");
        if (pageNums != null &amp;&amp; StringUtils.isNumeric(pageNums)) {
            pageNum = Integer.parseInt(pageNums);
        }
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    return pageNum;
}

/**
 * 设置默认每页大小
 * 
 * @return
 */
protected int getPageSize(HttpServletRequest request) {
    int pageSize = 10;    // 默认每页10条记录
    try {
        String pageSizes = request.getParameter("pageSize");
        if (pageSizes != null &amp;&amp; StringUtils.isNumeric(pageSizes)) {
            pageSize = Integer.parseInt(pageSizes);
        }
    } catch (NumberFormatException e) {
        e.printStackTrace();
    }
    return pageSize;
}

/**
 * @see Filter#init(FilterConfig)
 */
public void init(FilterConfig fConfig) throws ServletException {}

}

PageFilter.java

PageFilter 在 web.xml 中的配置:

    <!-- pagination filter -->
    <filter>
          <filter-name>PageFilter</filter-name>
          <filter-class>com.ems.filter.PageFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>PageFilter</filter-name>
          <url-pattern>/*</url-pattern>
      </filter-mapping>

OK,到此,PageHelper 的使用方法,基本结束。

PageHelper 项目地址:http://git.oschina.net/free/Mybatis_PageHelper

文档地址:http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/HowToUse.markdown