如果你也在用 MyBatis,建议尝试该分页插件,这一定是最方便使用的分页插件。
分页插件的必要性
互联网应用中,分页可谓无处不在,在每个需要展示数据的地方,都能找到分页的影子。在日常开发中,为了追求效率,通常使用数据库的物理分页。这时,对于一个业务逻辑SQL,大多数情况需要输出两段SQL来达到分页效果:count查询总数和limit分页,这无疑增加了大量的工作量。对于这种大量的、相似的、非业务逻辑的代码,抽象出公共插件是势在必行的。
分页插件原理
Mybatis给开发者提供了一个拦截器接口,只要实现了该接口,就可以在Mybatis执行SQL前,作一些自定义的操作。分页插件就是在此基础上开发出来的,对于一个需要分页的SQL,插件会拦截并生成两段SQL。举一个简单的例子:
原SQL:1
select * from table where a = '1'
拦截后的查询总数SQL:1
select count(*) from table where a = '1'
拦截后的分页SQL:1
select * from table where a = '1' limit 5,10
这样我们只需要根据业务逻辑开发原SQL,不需关心分页语法对原SQL的影响,拦截器已经为我们处理好了。更多拦截器的信息可以参考:
支持的数据库
该插件目前支持以下数据库的物理分页:
- Oracle
- Mysql
- MariaDB
- SQLite
- Hsqldb
- PostgreSQL
- DB2
- SqlServer(2005,2008)
- Informix
- H2
- SqlServer2012
- Derby
- Phoenix
与SpringMVC集成
Maven依赖
1 | <!-- 分页插件 --> |
Spring配置文件
只需要在原来配置Mybatis的SqlSessionFactoryBean
的地方加上分页插件的配置即可,具体区别请看以下的对比:
原来的配置方式:1
2
3
4
5
6
7
8
9<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource"/>
<property name="mapperLocations">
<list>
<value>classpath:mapper/*.xml</value>
<value>classpath:mapper/*/*.xml</value>
</list>
</property>
</bean>
加上分页插件的配置方式:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource"/>
<property name="mapperLocations">
<list>
<value>classpath:mapper/*.xml</value>
<value>classpath:mapper/*/*.xml</value>
</list>
</property>
<!-- 配置分页插件 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<value>
helperDialect=postgresql
reasonable=true
</value>
</property>
</bean>
</array>
</property>
</bean>
可以看到仅仅是加了<property name="plugins">
的配置。在<property name="properties">
里可以配置分页参数,一般情况下配置数据库类型helperDialect
即可。完整的参数如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样-->
<property name="offsetAsPageNum" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
<property name="pageSizeZero" value="true"/>
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true"/>
<!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
<!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 -->
<property name="params" value="pageNum=start;pageSize=limit;pageSizeZero=zero;reasonable=heli;count=contsql"/>
在代码中使用
在需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页,然后分页插件会把分页信息封装到PageInfo
中。1
2
3
4
5
6// startPage(第几页, 多少条数据)
PageHelper.startPage(pageIndex, pageSize);
// Mybatis查询方
List<InstanceVO> list = instanceDao.select(instance);
// 用PageInfo对结果进行包装
PageInfo pageInfo = new PageInfo(list);
以这种对原SQL无侵害的方法,就可以得到分页的效果和详细的分页信息。PageInfo包含了非常全面的分页属性:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public 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条数据"
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;
//总记录数
private long total;
//总页数
private int pages;
//结果集
private List<T> list;
//第一页
private int firstPage;
//前一页
private int prePage;
//下一页
private int nextPage;
//最后一页
private int lastPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
...
}
具体的例子
原SQL:1
2
3
4
5select
id,name,create_time,create_user_id,update_time,update_user_id,is_delete
from xxx.aaa
where
( is_delete = ? )
拦截后的SQL(源自Mybatis日志信息):1
2# count
select count(0) from xxx.aaa WHERE (is_delete = ?)
1 | # 分页 |
返回的json数据:
返回数据用一个自定义类Result
来封装,models
是业务数据,paging
是分页信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55{
"models":[
{
"createTime":1508890619000,
"createUserId":"888888",
"id":3,
"isDelete":0,
"name":"【TEST】",
"updateTime":1512373972000,
"updateUserId":"888888"
},
{
"createTime":1508890619000,
"createUserId":"888888",
"id":4,
"isDelete":0,
"name":"bbb",
"updateTime":1508891132000,
"updateUserId":"888888"
},
{
"createTime":1508890619000,
"createUserId":"888888",
"id":5,
"isDelete":0,
"name":"ccc",
"updateTime":1508891132000,
"updateUserId":"888888"
}
],
"paging":{
"endRow":3,
"firstPage":1,
"hasNextPage":true,
"hasPreviousPage":false,
"isFirstPage":true,
"isLastPage":false,
"lastPage":5,
"navigateFirstPage":1,
"navigateLastPage":5,
"navigatePages":8,
"navigatepageNums":[1,2,3,4,5],
"nextPage":2,
"pageNum":1,
"pageSize":3,
"pages":5,
"prePage":0,
"size":3,
"startRow":1,
"total":15
},
"resultCode":"100",
"success":true,
"valid":true
}
总结
使用分页插件时,不需要在分页的地方手写分页SQL和count的SQL,不需要更改已有的业务代码,只需要在执行SQL前调用一句代码即可实现分页,并得到丰富的分页信息。有了这些分页信息,前端可以选用多种分页方法,非常方便!