为提供Mybatis PageHelper 更优雅地调用方式, 笔者开发了Mybatis PageHelper Plus 插件.
1. 引入依赖
笔者基于Mybatis 3.4.6 和 pageheler 5.1.10 开发的此插件
<properties>
<mybatis.version>3.4.6</mybatis.version>
<mybatis.pagehelper.version>5.1.10</mybatis.pagehelper.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${mybatis.pagehelper.version}</version>
</dependency>
</dependencies>
2. 源码
笔者一共新建了三个类
- PageBounds: 用于设定分页的页码和每页大小
- PageResulst: 和PageInfo 完全等价, 用于自动封装分页信息和查询结果集.
- PageInterceptorPlus: 核心拦截器.
2.1 PageBounds
分页条件, 设置页码和每页条数.
public class PageBounds {
/** 页码 **/
private int page;
/** 每页大小 **/
private int pageSize;
public PageBounds() {
}
public PageBounds(int page, int pageSize) {
this.page = page;
this.pageSize = pageSize;
}
public static PageBounds of(int page, int pageSize) {
return new PageBounds(page, pageSize);
}
//setter, getter..
}
2.2 PageInterceptorPlus
核心拦截器, 基础PageInterceptor
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class PageInterceptorPlus extends PageInterceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 默认不开启分页
boolean pageAble = false;
// 解析参数, 看是否包含PageBounds参数
Object parameter = invocation.getArgs()[1];
PageBounds pagerBounds = null;
if( parameter instanceof MapperMethod.ParamMap){
MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;
Set<Map.Entry<String,Object>> set = paramMap.entrySet();
for (Map.Entry<String, Object> entry : set)
if (entry.getValue() instanceof PageBounds) {
pagerBounds = (PageBounds) entry.getValue();
}
} else if (parameter instanceof PageBounds) {
pagerBounds = (PageBounds) parameter;
}
// 如果pageBounds 不为空, 则表示需要分页
if (pagerBounds != null) {
PageHelper.startPage(pagerBounds.getPage(), pagerBounds.getPageSize());
pageAble = true;
}
// 执行查询方法
Object object = super.intercept(invocation);
// 封装结果集
if (pageAble) {
return new PageResult<>((List) object);
}else {
return object;
}
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
}
}
2.3 PageResult
- 笔者本想在拦截器中返回PageInfo 对象, 但是无奈PageInfo 并非是List 类型, 会报异常: Cause: java.lang.ClassCastException: com.github.pagehelper.PageInfo cannot be cast to java.util.List. 所以只能新创建一个PageResult 对象.
- 此类由PageInfo 对象复制而来, 仅仅修改了total 参数的处理方式.
public class PageResult<T> extends ArrayList<T> {
private static final long serialVersionUID = 1412759446332294208L;
//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//由于startRow和endRow不常用,这里说个具体的用法
//可以在页面中"显示startRow到endRow 共size条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总页数
private int pages;
//前一页
private int prePage;
//下一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航条上的第一页
private int navigateFirstPage;
//导航条上的最后一页
private int navigateLastPage;
private long total;
public PageResult() {
}
/**
* 包装Page对象
*
* @param list
*/
public PageResult(List<T> list) {
this(list, 8);
}
/**
* 包装Page对象
*
* @param list page结果
* @param navigatePages 页码数量
*/
public PageResult(List<T> list, int navigatePages) {
super(list);
if (list instanceof Page) {
Page page = (Page) list;
this.pageNum = page.getPageNum();
this.pageSize = page.getPageSize();
this.pages = page.getPages();
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;
}
total = page.getTotal();
} else if (list instanceof Collection) {
this.pageNum = 1;
this.pageSize = list.size();
this.pages = this.pageSize > 0 ? 1 : 0;
this.size = list.size();
this.startRow = 0;
this.endRow = list.size() > 0 ? list.size() - 1 : 0;
}
if (list instanceof Collection) {
this.navigatePages = navigatePages;
//计算导航页
calcNavigatepageNums();
//计算前后页,第一页,最后一页
calcPage();
//判断页面边界
judgePageBoudary();
}
}
public static <T> PageInfo<T> of(List<T> list) {
return new PageInfo<T>(list);
}
public static <T> PageInfo<T> of(List<T> list, int navigatePages) {
return new PageInfo<T>(list, navigatePages);
}
/**
* 计算导航页
*/
private void calcNavigatepageNums() {
//当总页数小于或等于导航页码数时
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 void calcPage() {
if (navigatepageNums != null && navigatepageNums.length > 0) {
navigateFirstPage = navigatepageNums[0];
navigateLastPage = navigatepageNums[navigatepageNums.length - 1];
if (pageNum > 1) {
prePage = pageNum - 1;
}
if (pageNum < pages) {
nextPage = pageNum + 1;
}
}
}
/**
* 判定页面边界
*/
private void judgePageBoudary() {
isFirstPage = pageNum == 1;
isLastPage = pageNum == pages || pages == 0;
hasPreviousPage = pageNum > 1;
hasNextPage = pageNum < pages;
}
//setter,gettter, toString 方法..
}