以前使用ibatis/mybatis,都是自己手写sql语句进行物理分页,虽然稍微有点麻烦,但是都习惯了。最近试用了下mybatis的分页插件 PageHelper,感觉还不错吧。记录下其使用方法。
1.引入依赖jar包:
com.github.pagehelper
pagehelper
3.7.5
2.配置分页拦截器
PageHelper的原理是基于拦截器实现的。拦截器的配置有两种方法,一种是在mybatis的配置文件中配置,一种是直接在spring的配置文件中进行:
1)在mybatis-config.xml文件中配置:
这里要注意 在mybatis-config.xml文件中的位置,必须要符合 http://mybatis.org/dtd/mybatis-3-config.dtd 中指定的顺序:
objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
不然会报错。
当然mybatis-config.xml的位置,我们需要在spring的配置文件中进行指定:
2)如果mybatis没有mybatis-config.xml文件,那么就只能直接在spring的配置文件中配置了:
classpath:config/mapper/*.xml
dialect=mysql
到这里PageHelper所需要的配置已经完成,下面还需要在serviceImpl类中加入分页参数的代码:
3. 向拦截器传递分页参数
我们首先看下不分页的serviceImpl代码:
@Overridepublic ListgetUserByNoAndEmail(String no, String email) {
Map 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 implementsSerializable {private static final long serialVersionUID = 1L;//当前页
private intpageNum;//每页的数量
private intpageSize;//当前页的数量
private intsize;//由于startRow和endRow不常用,这里说个具体的用法//可以在页面中"显示startRow到endRow 共size条数据"//当前页面第一个元素在数据库中的行号
private intstartRow;//当前页面最后一个元素在数据库中的行号
private intendRow;//总记录数
private longtotal;//总页数
private intpages;//结果集
private Listlist;//第一页
private intfirstPage;//前一页
private intprePage;//下一页
private intnextPage;//最后一页
private intlastPage;//是否为第一页
private boolean isFirstPage = false;//是否为最后一页
private boolean isLastPage = false;//是否有前一页
private boolean hasPreviousPage = false;//是否有下一页
private boolean hasNextPage = false;//导航页码数
private intnavigatePages;//所有导航页号
private int[] navigatepageNums;/*** 包装Page对象
*
*@paramlist*/
public PageInfo(Listlist) {this(list, 8);
}/*** 包装Page对象
*
*@paramlist page结果
*@paramnavigatePages 页码数量*/
public PageInfo(List list, intnavigatePages) {if (list instanceofPage) {
Page page=(Page) list;this.pageNum =page.getPageNum();this.pageSize =page.getPageSize();this.total =page.getTotal();this.pages =page.getPages();this.list =page;this.size =page.size();//由于结果是>startRow的,所以实际的需要+1
if (this.size == 0) {this.startRow = 0;this.endRow = 0;
}else{this.startRow = page.getStartRow() + 1;//计算实际的endRow(最后一页的时候特殊)
this.endRow = this.startRow - 1 + this.size;
}this.navigatePages =navigatePages;//计算导航页
calcNavigatepageNums();//计算前后页,第一页,最后一页
calcPage();//判断页面边界
judgePageBoudary();
}
}/*** 计算导航页*/
private voidcalcNavigatepageNums() {//当总页数小于或等于导航页码数时
if (pages <=navigatePages) {
navigatepageNums= new int[pages];for (int i = 0; i < pages; i++) {
navigatepageNums[i]= i + 1;
}
}else { //当总页数大于导航页码数时
navigatepageNums = new int[navigatePages];int startNum = pageNum - navigatePages / 2;int endNum = pageNum + navigatePages / 2;if (startNum < 1) {
startNum= 1;//(最前navigatePages页
for (int i = 0; i < navigatePages; i++) {
navigatepageNums[i]= startNum++;
}
}else if (endNum >pages) {
endNum=pages;//最后navigatePages页
for (int i = navigatePages - 1; i >= 0; i--) {
navigatepageNums[i]= endNum--;
}
}else{//所有中间页
for (int i = 0; i < navigatePages; i++) {
navigatepageNums[i]= startNum++;
}
}
}
}/*** 计算前后页,第一页,最后一页*/
private voidcalcPage() {if (navigatepageNums != null && navigatepageNums.length > 0) {
firstPage= navigatepageNums[0];
lastPage= navigatepageNums[navigatepageNums.length - 1];if (pageNum > 1) {
prePage= pageNum - 1;
}if (pageNum
nextPage= pageNum + 1;
}
}
}/*** 判定页面边界*/
private voidjudgePageBoudary() {
isFirstPage= pageNum == 1;
isLastPage= pageNum ==pages;
hasPreviousPage= pageNum > 1;
hasNextPage= pageNum
}public void setPageNum(intpageNum) {this.pageNum =pageNum;
}public intgetPageNum() {returnpageNum;
}public intgetPageSize() {returnpageSize;
}public intgetSize() {returnsize;
}public intgetStartRow() {returnstartRow;
}public intgetEndRow() {returnendRow;
}public longgetTotal() {returntotal;
}public intgetPages() {returnpages;
}public ListgetList() {returnlist;
}public intgetFirstPage() {returnfirstPage;
}public intgetPrePage() {returnprePage;
}public intgetNextPage() {returnnextPage;
}public intgetLastPage() {returnlastPage;
}public booleanisIsFirstPage() {returnisFirstPage;
}public booleanisIsLastPage() {returnisLastPage;
}public booleanisHasPreviousPage() {returnhasPreviousPage;
}public booleanisHasNextPage() {returnhasNextPage;
}public intgetNavigatePages() {returnnavigatePages;
}public int[] getNavigatepageNums() {returnnavigatepageNums;
}
@OverridepublicString toString() {final StringBuffer sb = new StringBuffer("PageInfo{");
sb.append("pageNum=").append(pageNum);
sb.append(", pageSize=").append(pageSize);
sb.append(", size=").append(size);
sb.append(", startRow=").append(startRow);
sb.append(", endRow=").append(endRow);
sb.append(", total=").append(total);
sb.append(", pages=").append(pages);
sb.append(", list=").append(list);
sb.append(", firstPage=").append(firstPage);
sb.append(", prePage=").append(prePage);
sb.append(", nextPage=").append(nextPage);
sb.append(", lastPage=").append(lastPage);
sb.append(", isFirstPage=").append(isFirstPage);
sb.append(", isLastPage=").append(isLastPage);
sb.append(", hasPreviousPage=").append(hasPreviousPage);
sb.append(", hasNextPage=").append(hasNextPage);
sb.append(", navigatePages=").append(navigatePages);
sb.append(", navigatepageNums=");if (navigatepageNums == null) sb.append("null");else{
sb.append('[');for (int i = 0; i < navigatepageNums.length; ++i)
sb.append(i== 0 ? "" : ", ").append(navigatepageNums[i]);
sb.append(']');
}
sb.append('}');returnsb.toString();
}
}
PageInfo.java
因为PageInfo.java只是一个示例,所以他定义得有点重量级,属性有点多,我们可以参考它,定义适合我们自己的PageBean, 比如如下定义:
public class PageBean implementsSerializable {private static final long serialVersionUID = 8656597559014685635L;private long total; //总记录数
private List list; //结果集
private int pageNum; //第几页
private int pageSize; //每页记录数
private int pages; //总页数
private int size; //当前页的数量 <= pageSize,该属性来自ArrayList的size属性
/*** 包装Page对象,因为直接返回Page对象,在JSON处理以及其他情况下会被当成List来处理,
* 而出现一些问题。
*@paramlist page结果
*@paramnavigatePages 页码数量*/
public PageBean(Listlist) {if (list instanceofPage) {
Page page = (Page) list;this.pageNum =page.getPageNum();this.pageSize =page.getPageSize();this.total =page.getTotal();this.pages =page.getPages();this.list =page;this.size =page.size();
}
}public longgetTotal() {returntotal;
}public void setTotal(longtotal) {this.total =total;
}public ListgetList() {returnlist;
}public void setList(Listlist) {this.list =list;
}public intgetPageNum() {returnpageNum;
}public void setPageNum(intpageNum) {this.pageNum =pageNum;
}public intgetPageSize() {returnpageSize;
}public void setPageSize(intpageSize) {this.pageSize =pageSize;
}public intgetPages() {returnpages;
}public void setPages(intpages) {this.pages =pages;
}public intgetSize() {returnsize;
}public void setSize(intsize) {this.size =size;
}
}
PageBean.java
因为分页查询结果返回的是一个 Page 对象,而 Page 对象继承自ArrayList,但是如果我们直接返回ArrayList的话,在一些场景下回遇到问题,比如在JSON处理Page类型的结果时,会被当成List来JSON格式化,会丢弃 Page 对象的所有扩展属性,所以这里我们要将分页的结果 Page 类型转换成我们自己定义的 PageBean. 我们自己定义的PageBean没有继承ArrayList,而是包含一个List属性来保存分页结果。所以避免前面的问题。
2)修改 serviceImpl中的代码:
@Overridepublic PageBeangetUserByNoAndEmail(String no, String email) {
Map map = new HashMap<>();
map.put("no", no);
map.put("email", email);
PageHelper.startPage(PaginationContext.getPageNum(), PaginationContext.getPageSize());
List list = this.userMapper.getUserByNoAndEmail(map);return new PageBean(list);
}
我们只需要使用 PageHelper.startPage(pageNum, pageSize); 函数来指定 pageNum(第几页) 和 pageSize(每页显示几条记录) 两个参数。然后调用原来的查询,就进行了分页。最后将返回的List,转换成 PageBean类型的结果即可。前台页面就可以根据PageBean中包括的属性来进行分页显示了。
上面的 PaginationContext 是基于 ThreadLocal 来传递分页参数的一个工具类,其实现如下:
public classPaginationContext {//定义两个threadLocal变量:pageNum和pageSize
private static ThreadLocal pageNum = new ThreadLocal(); //保存第几页
private static ThreadLocal pageSize = new ThreadLocal(); //保存每页记录条数
/** pageNum :get、set、remove*/
public static intgetPageNum() {
Integer pn=pageNum.get();if (pn == null) {return 0;
}returnpn;
}public static void setPageNum(intpageNumValue) {
pageNum.set(pageNumValue);
}public static voidremovePageNum() {
pageNum.remove();
}/** pageSize :get、set、remove*/
public static intgetPageSize() {
Integer ps=pageSize.get();if (ps == null) {return 0;
}returnps;
}public static void setPageSize(intpageSizeValue) {
pageSize.set(pageSizeValue);
}public static voidremovePageSize() {
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 && 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 && 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中的配置:
PageFilter
com.ems.filter.PageFilter
PageFilter
/*
OK,到此,PageHelper的使用方法,基本结束。
PageHelper 项目地址:http://git.oschina.net/free/Mybatis_PageHelper
文档地址:http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/HowToUse.markdown