如果你也在用 MyBatis,建议尝试该分页插件,这一定是 最方便 使用的分页插件。
互联网应用中,分页可谓无处不在,在每个需要展示数据的地方,都能找到分页的影子。在日常开发中,为了追求效率,通常使用数据库的物理分页。这时,对于一个业务逻辑SQL,大多数情况需要输出两段SQL来达到分页效果:count查询总数和limit分页,这无疑增加了大量的工作量。对于这种大量的、相似的、非业务逻辑的代码,抽象出公共插件是势在必行的。
Mybatis给开发者提供了一个拦截器接口,只要实现了该接口,就可以在Mybatis执行SQL前,作一些自定义的操作。分页插件就是在此基础上开发出来的,对于一个需要分页的SQL,插件会拦截并生成两段SQL。举一个简单的例子:
原SQL:
select * from table where a = '1'
拦截后的查询总数SQL:
select count(*) from table where a = '1'
拦截后的分页SQL:
select * from table where a = '1' limit 5,10
这样我们只需要根据业务逻辑开发原SQL,不需关心分页语法对原SQL的影响,拦截器已经为我们处理好了。更多拦截器的信息可以参考:
该插件目前支持以下数据库的物理分页:
<!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.0.0</version> </dependency>
只需要在原来配置Mybatis的 SqlSessionFactoryBean
的地方加上分页插件的配置即可,具体区别请看以下的对比:
原来的配置方式:
<beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"> <propertyname="dataSource"ref="dynamicDataSource"/> <propertyname="mapperLocations"> <list> <value>classpath:mapper/*.xml</value> <value>classpath:mapper/*/*.xml</value> </list> </property> </bean>
加上分页插件的配置方式:
<beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"> <propertyname="dataSource"ref="dynamicDataSource"/> <propertyname="mapperLocations"> <list> <value>classpath:mapper/*.xml</value> <value>classpath:mapper/*/*.xml</value> </list> </property> <!-- 配置分页插件 --> <propertyname="plugins"> <array> <beanclass="com.github.pagehelper.PageInterceptor"> <propertyname="properties"> <value> helperDialect=postgresql reasonable=true </value> </property> </bean> </array> </property> </bean>
可以看到仅仅是加了 <property name="plugins">
的配置。在 <property name="properties">
里可以配置分页参数,一般情况下配置数据库类型 helperDialect
即可。完整的参数如下:
<!-- 该参数默认为false --> <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 --> <!-- 和startPage中的pageNum效果一样--> <propertyname="offsetAsPageNum"value="true"/> <!-- 该参数默认为false --> <!-- 设置为true时,使用RowBounds分页会进行count查询 --> <propertyname="rowBoundsWithCount"value="true"/> <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 --> <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)--> <propertyname="pageSizeZero"value="true"/> <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 --> <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 --> <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 --> <propertyname="reasonable"value="true"/> <!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 --> <!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 --> <!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 --> <propertyname="params"value="pageNum=start;pageSize=limit;pageSizeZero=zero;reasonable=heli;count=contsql"/>
在需要进行分页的Mybatis方法前调用PageHelper.startPage静态方法即可,紧跟在这个方法后的第一个Mybatis查询方法会被进行分页,然后分页插件会把分页信息封装到 PageInfo
中。
// startPage(第几页, 多少条数据) PageHelper.startPage(pageIndex, pageSize); // Mybatis查询方 List<InstanceVO> list = instanceDao.select(instance); // 用PageInfo对结果进行包装 PageInfo pageInfo = new PageInfo(list);
以这种对原SQL无侵害的方法,就可以得到分页的效果和详细的分页信息。PageInfo包含了非常全面的分页属性:
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条数据" //当前页面第一个元素在数据库中的行号 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:
select id,name,create_time,create_user_id,update_time,update_user_id,is_delete from xxx.aaa where ( is_delete = ? )
拦截后的SQL(源自Mybatis日志信息):
# count select count(0) from xxx.aaa WHERE (is_delete = ?)
# 分页 select id,name,create_time,create_user_id,update_time,update_user_id,is_delete from xxx.aaa where ( is_delete = ? ) LIMIT 3
返回的json数据:
返回数据用一个自定义类 Result
来封装, models
是业务数据, paging
是分页信息。
{ "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前调用一句代码即可实现分页,并得到丰富的分页信息。有了这些分页信息,前端可以选用多种分页方法,非常方便!