11.1 准备工作
分页是查询功能中最常用的,基本上是项目必须的。在学习 SpringMVC+Spring+SpringData/JPA 的时候曾经接触了 SpringData中的接口类 PagingAndSortingRepository 。也是用于分页的,因为没有系统学习 SpringData 和 JPA,对该接口的理解不是很透彻(虽然老师在视频中非常详细的分析了源码,说来还是我笨吧)。后期在练习的过程中也总是出现自己无法判断原因的错误。这次因为不是采用的 SpringData/JPA 技术,所以分页选择了 Mybatis 的插件 pageHelper。这个插件看起来相对简单些,而且个人感觉封装的信息非常完整,也很方便调用,非常利于前台搭建。
官网的几个链接
11.2 POM 文件
增加下面的依赖到 pom.xml 文件。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
11.3 配置拦截器插件
特别注意,新版拦截器是 com.github.pagehelper.PageInterceptor。 com.github.pagehelper.PageHelper 现在是一个特殊的 dialect 实现类,是分页插件的默认实现类,提供了和以前相同的用法。
11.3.1 在 MyBatis 配置 xml 中配置拦截器插件
注释里面的内容非常重要,一定要好好看,最好复制到项目的配置文件中作为提醒。
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
11.3.2 在 Spring 配置文件中配置拦截器插件
使用 spring 的属性配置方式,可以使用 plugins 属性像下面这样配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注意其他配置 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<!--使用下面的方式配置参数,一行配置一个 -->
<value>
params=value1
</value>
</property>
</bean>
</array>
</property>
</bean>
这两种方法随便用一个就可以,我用的第一种。
11.4 使用 PageHelper 的方法
在 PageHelper 官网的文档中对其使用方法有一大段的说明,调用的方式也是多种多样,视频中老师使用的方法是下面这种,在粗略看过其他方法后,考虑到我的能力和学习成本,决定先研究这种方法,毕竟官网的推荐方法之一应该更靠谱。
PageHelper.startPage 静态方法调用
除了 PageHelper.startPage 方法外,还提供了类似用法的 PageHelper.offsetPage 方法。
在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个MyBatis 查询方法会被进行分页。
例一:
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
//紧跟着的第一个select方法会被分页
List<Country> list = countryMapper.selectIf(1);
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>
assertEquals(182, ((Page) list).getTotal());
例二:
//request: url?pageNum=1&pageSize=10
//支持 ServletRequest,Map,POJO 对象,需要配合 params 参数
PageHelper.startPage(request);
//紧跟着的第一个select方法会被分页
List<Country> list = countryMapper.selectIf(1);
//后面的不会被分页,除非再次调用PageHelper.startPage
List<Country> list2 = countryMapper.selectIf(null);
//list1
assertEquals(2, list.get(0).getId());
assertEquals(10, list.size());
//分页时,实际返回的结果list类型是Page<E>,如果想取出分页信息,需要强制转换为Page<E>,
//或者使用PageInfo类(下面的例子有介绍)
assertEquals(182, ((Page) list).getTotal());
//list2
assertEquals(1, list2.get(0).getId());
assertEquals(182, list2.size());
例三,使用PageInfo的用法:
这种方法更适合向前台传送分页数据。
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage());
重要提示(还是写在这里吧,省的去官网查了)
- PageHelper.startPage 方法:
只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会被分页。- 请不要配置多个分页插件
请不要在系统中配置多个分页插件(使用Spring时,mybatis-config.xml和Spring< bean >配置方式,请选择其中一种,不要同时配置多个分页插件)!- 分页插件不支持带有for update语句的分页
对于带有for update的sql,会抛出运行时异常,对于这样的sql建议手动分页,毕竟这样的sql需要重视。- 分页插件不支持嵌套结果映射
由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。
11.5 测试插件
使用如下测试代码测试插件效果。限于篇幅,其中涉及的 Controller 和 Service 部分留到下一篇介绍。实际上,这两部分应该和本篇内容一道完成,否则无法完成测试。
package com.hh.ssm.test;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.github.pagehelper.PageInfo;
import com.hh.ssm.bean.Employee;
/**
* 使用SpringMVC提供的测试
*
* @author HH
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath:applicationContext.xml",
"file:src/main/webapp/WEB-INF/dispatcherServlet-servlet.xml" })
public class MvcTest {
MockMvc mockMvc;
@Autowired
WebApplicationContext context;
@Before
public void initMockmvc() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
@Test
public void testGetAllEmployeeWithDept() throws Exception {
String pageNum = "5";
// 模拟发出 /employees 的 get 请求,传递参数pageNum,返回结果
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get("/employees").param("pageNum", pageNum))
.andReturn();
// 把结果封装到 request 域
MockHttpServletRequest request = result.getRequest();
// 封装 PageInfo 对象
PageInfo pageInfo = (PageInfo) request.getAttribute("pageInfo");
// 使用 PageInfo 对象的各种方法得到分页数据
System.out.println("Total :" + pageInfo.getTotal());
System.out.println("PageNum :" + pageInfo.getPageNum());
System.err.println("Pages :" + pageInfo.getPages());
System.out.println("PageSize :" + pageInfo.getPageSize());
System.out.println("getStartRow :" + pageInfo.getStartRow());
System.out.println("getEndRow :" + pageInfo.getEndRow());
List<Employee> employees = pageInfo.getList();
for (Employee employee : employees) {
System.out.println(employee);
}
ArrayList<Integer> navigatePageList = new ArrayList<Integer>();
int[] navigatePageNums = pageInfo.getNavigatepageNums();
System.out.println("navagatePageNums : "+ navigatePageNums);
for (int i = 0; i < navigatePageNums.length; i++) {
navigatePageList.add(navigatePageNums[i]);
}
System.out.println("navigatePageList: "+navigatePageList.toString());
}
}
运行结果如下:
可以看出分页成功了!!!现在是该考虑前台页面的时候了。