当前位置:   article > 正文

分页插件 Pagehelper-Spring Boot 依赖_pagehelper依赖

pagehelper依赖

一、分页插件 Pagehelper

PageHelperMybatis的一个分页插件,非常好用!

1.1 Spring Boot 依赖

  1. <!-- pagehelper 分页插件-->
  2. <dependency>
  3. <groupId>com.github.pagehelper</groupId>
  4. <artifactId>pagehelper-spring-boot-starter</artifactId>
  5. <version>1.2.12</version>
  6. </dependency>

也可以这么引入

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper</artifactId>
  4. <version>latest version</version>
  5. </dependency>

1.2 PageHelper 配置

配置文件增加PageHelper的配置,主要设置了分页方言和支持接口参数传递分页参数,如下:

  1. pagehelper:
  2. # 指定数据库
  3. helper-dialect: mysql
  4. # 默认是false。启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages(最大页数)会查询最后一页。禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据
  5. reasonable: false
  6. # 是否支持接口参数来传递分页参数,默认false
  7. support-methods-arguments: true
  8. # 为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
  9. params: count=countSql
  10. row-bounds-with-count: true

项目完整配置文件详见文mybatis-pagehelper

1.3 如何分页

只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会自动分页!!!!

  1. @Test
  2. public void selectForPage() {
  3. // 第几页
  4. int currentPage = 2;
  5. // 每页数量
  6. int pageSize = 5;
  7. // 排序
  8. String orderBy = "id desc";
  9. PageHelper.startPage(currentPage, pageSize, orderBy);
  10. List<UserInfoPagehelperDO> users = userInfoPagehelperMapper.selectList();
  11. PageInfo<UserInfoPagehelperDO> userPageInfo = new PageInfo<>(users);
  12. log.info("userPageInfo:{}", userPageInfo);
  13. }
...: userPageInfo:PageInfo{pageNum=2, pageSize=5, size=1, startRow=6, endRow=6, total=6, pages=2, list=Page{count=true, pageNum=2, pageSize=5, startRow=5, endRow=10, total=6, pages=2, reasonable=false, pageSizeZero=false}[UserInfoPagehelperDO{id=1, userName='null', age=22, createTime=null}], prePage=1, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}

这里的返回结果包括数据、是否为第一页/最后一页、总页数、总记录数,详见Mybatis-PageHelper

Pagehelper 分页完整示例

二、Mybatis 拦截器实现分页

2.1 Mybatis 拦截器

Mybatis 官网【插件】部分有以下描述:

  1. 通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
  2. MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. StatementHandler (prepare, parameterize, batch, update, query)

即:我们可以通过拦截器的方式,实现MyBatis插件(数据分页)

接下来重点演示我如何使用了拦截器实现分页。

2.2 调用形式

在看如何实现之前,我们先看看如何使用:

照抄 PageHelper 的设计,先调用一个静态方法,对下面第一个方法的sql语句进行拦截,在new一个分页对象时自动处理。

  1. @Test
  2. public void selectForPage() {
  3. // 该查询进行分页,指定第几页和每页数量
  4. PageInterceptor.startPage(1,2);
  5. List<UserInfoDO> all = dao.findAll();
  6. PageResult<UserInfoDO> result = new PageResult<>(all);
  7. // 分页结果打印
  8. System.out.println("总记录数:" + result.getTotal());
  9. System.out.println(result.getData().toString());
  10. }

然后我们主要看看实现步骤。

2.3 数据库方言

定义好一个方言接口,不同的数据使用不同的方言实现

  • Dialect.java
  1. public interface Dialect {
  2. /**
  3. * 获取count SQL语句
  4. *
  5. * @param targetSql
  6. * @return
  7. */
  8. default String getCountSql(String targetSql) {
  9. return String.format("select count(1) from (%s) tmp_count", targetSql);
  10. }
  11. /**
  12. * 获取limit SQL语句
  13. * @param targetSql
  14. * @param offset
  15. * @param limit
  16. * @return
  17. */
  18. String getLimitSql(String targetSql, int offset, int limit);
  19. }
  • Mysql 分页方言
  1. @Component
  2. public class MysqlDialect implements Dialect{
  3. private static final String PATTERN = "%s limit %s, %s";
  4. private static final String PATTERN_FIRST = "%s limit %s";
  5. @Override
  6. public String getLimitSql(String targetSql, int offset, int limit) {
  7. if (offset == 0) {
  8. return String.format(PATTERN_FIRST, targetSql, limit);
  9. }
  10. return String.format(PATTERN, targetSql, offset, limit);
  11. }
  12. }

2.4 拦截器核心逻辑

  1.  优亿在线注册:www.jintianxuesha.com
  2.  百事2:www.baihuayl7.cn

 

  • 分页辅助参数内部类 PageParam.java
  1. public static class PageParam {
  2. // 当前页
  3. int pageNum;
  4. // 分页开始位置
  5. int offset;
  6. // 分页数量
  7. int limit;
  8. // 总数
  9. public int totalSize;
  10. // 总页数
  11. public int totalPage;
  12. }
  • 查询总记录数
  1. private long queryTotal(MappedStatement mappedStatement, BoundSql boundSql) throws SQLException {
  2. Connection connection = null;
  3. PreparedStatement countStmt = null;
  4. ResultSet rs = null;
  5. try {
  6. connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
  7. String countSql = this.dialect.getCountSql(boundSql.getSql());
  8. countStmt = connection.prepareStatement(countSql);
  9. BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql,
  10. boundSql.getParameterMappings(), boundSql.getParameterObject());
  11. setParameters(countStmt, mappedStatement, countBoundSql, boundSql.getParameterObject());
  12. rs = countStmt.executeQuery();
  13. long totalCount = 0;
  14. if (rs.next()) {
  15. totalCount = rs.getLong(1);
  16. }
  17. return totalCount;
  18. } catch (SQLException e) {
  19. log.error("查询总记录数出错", e);
  20. throw e;
  21. } finally {
  22. if (rs != null) {
  23. try {
  24. rs.close();
  25. } catch (SQLException e) {
  26. log.error("exception happens when doing: ResultSet.close()", e);
  27. }
  28. }
  29. if (countStmt != null) {
  30. try {
  31. countStmt.close();
  32. } catch (SQLException e) {
  33. log.error("exception happens when doing: PreparedStatement.close()", e);
  34. }
  35. }
  36. if (connection != null) {
  37. try {
  38. connection.close();
  39. } catch (SQLException e) {
  40. log.error("exception happens when doing: Connection.close()", e);
  41. }
  42. }
  43. }
  44. }
  • 对分页SQL参数?设值
  1. private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
  2. Object parameterObject) throws SQLException {
  3. ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
  4. parameterHandler.setParameters(ps);
  5. }
  • 利用方言接口替换原始的SQL语句
  1. private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
  2. MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
  3. builder.resource(ms.getResource());
  4. builder.fetchSize(ms.getFetchSize());
  5. builder.statementType(ms.getStatementType());
  6. builder.keyGenerator(ms.getKeyGenerator());
  7. if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
  8. StringBuffer keyProperties = new StringBuffer();
  9. for (String keyProperty : ms.getKeyProperties()) {
  10. keyProperties.append(keyProperty).append(",");
  11. }
  12. keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
  13. builder.keyProperty(keyProperties.toString());
  14. }
  15. //setStatementTimeout()
  16. builder.timeout(ms.getTimeout());
  17. //setStatementResultMap()
  18. builder.parameterMap(ms.getParameterMap());
  19. //setStatementResultMap()
  20. builder.resultMaps(ms.getResultMaps());
  21. builder.resultSetType(ms.getResultSetType());
  22. //setStatementCache()
  23. builder.cache(ms.getCache());
  24. builder.flushCacheRequired(ms.isFlushCacheRequired());
  25. builder.useCache(ms.isUseCache());
  26. return builder.build();
  27. }
  • 计算总页数
  1. public int countPage(int totalSize, int offset) {
  2. int totalPageTemp = totalSize / offset;
  3. int plus = (totalSize % offset) == 0 ? 0 : 1;
  4. totalPageTemp = totalPageTemp + plus;
  5. if (totalPageTemp <= 0) {
  6. totalPageTemp = 1;
  7. }
  8. return totalPageTemp;
  9. }
  • 供调用的静态分页方法

我这里设计的,页数是从1开始的,如果习惯用0开始,可以自己修改。

  1. public static void startPage(int pageNum, int pageSize) {
  2. int offset = (pageNum-1) * pageSize;
  3. int limit = pageSize;
  4. PageInterceptor.PageParam pageParam = new PageInterceptor.PageParam();
  5. pageParam.offset = offset;
  6. pageParam.limit = limit;
  7. pageParam.pageNum = pageNum;
  8. PARAM_THREAD_LOCAL.set(pageParam);
  9. }

2.5 分页结果集

为了便于结果封装,我这里自己封装了一个比较全的分页结果集,包含太多的东西了,自己慢慢看下面的属性吧(自认为比较全了,欢迎打脸)

  1. public class PageResult<T> implements Serializable {
  2. /**
  3. * 是否为第一页
  4. */
  5. private Boolean isFirstPage = false;
  6. /**
  7. * 是否为最后一页
  8. */
  9. private Boolean isLastPage = false;
  10. /**
  11. * 当前页
  12. */
  13. private Integer pageNum;
  14. /**
  15. * 每页的数量
  16. */
  17. private Integer pageSize;
  18. /**
  19. * 总记录数
  20. */
  21. private Integer totalSize;
  22. /**
  23. * 总页数
  24. */
  25. private Integer totalPage;
  26. /**
  27. * 结果集
  28. */
  29. private List<T> data;
  30. public PageResult() {
  31. }
  32. public PageResult(List<T> data) {
  33. this.data = data;
  34. PageInterceptor.PageParam pageParam = PageInterceptor.PARAM_THREAD_LOCAL.get();
  35. if (pageParam != null) {
  36. pageNum = pageParam.pageNum;
  37. pageSize = pageParam.limit;
  38. totalSize = pageParam.totalSize;
  39. totalPage = pageParam.totalPage;
  40. isFirstPage = (pageNum == 1);
  41. isLastPage = (pageNum == totalPage);
  42. PageInterceptor.PARAM_THREAD_LOCAL.remove();
  43. }
  44. }
  45. public Integer getPageNum() {
  46. return pageNum;
  47. }
  48. public void setPageNum(Integer pageNum) {
  49. this.pageNum = pageNum;
  50. }
  51. public Integer getPageSize() {
  52. return pageSize;
  53. }
  54. public void setPageSize(Integer pageSize) {
  55. this.pageSize = pageSize;
  56. }
  57. public Integer getTotalSize() {
  58. return totalSize;
  59. }
  60. public void setTotalSize(Integer totalSize) {
  61. this.totalSize = totalSize;
  62. }
  63. public Integer getTotalPage() {
  64. return totalPage;
  65. }
  66. public void setTotalPage(Integer totalPage) {
  67. this.totalPage = totalPage;
  68. }
  69. public List<T> getData() {
  70. return data;
  71. }
  72. public void setData(List<T> data) {
  73. this.data = data;
  74. }
  75. public Boolean getFirstPage() {
  76. return isFirstPage;
  77. }
  78. public void setFirstPage(Boolean firstPage) {
  79. isFirstPage = firstPage;
  80. }
  81. public Boolean getLastPage() {
  82. return isLastPage;
  83. }
  84. public void setLastPage(Boolean lastPage) {
  85. isLastPage = lastPage;
  86. }
  87. @Override
  88. public String toString() {
  89. return "PageResult{" +
  90. "isFirstPage=" + isFirstPage +
  91. ", isLastPage=" + isLastPage +
  92. ", pageNum=" + pageNum +
  93. ", pageSize=" + pageSize +
  94. ", totalSize=" + totalSize +
  95. ", totalPage=" + totalPage +
  96. ", data=" + data +
  97. '}';
  98. }
  99. }

2.6 简单测试下

  1. @Test
  2. public void selectForPage() {
  3. // 该查询进行分页,指定第几页和每页数量
  4. PageInterceptor.startPage(1,4);
  5. List<UserInfoDO> all = userMapper.findAll();
  6. PageResult<UserInfoDO> result = new PageResult<>(all);
  7. // 分页结果打印
  8. log.info("总记录数:{}", result.getTotalSize());
  9. log.info("list:{}", result.getData());
  10. log.info("result:{}", result);
  11. }

使用方法基本1.3完全一致吧,只是封装成了我自己的分页结果集。

  • 日志如下:
  1. ....: ==> Preparing: SELECT id, user_name, age, create_time FROM user_info_pageable limit 4
  2. ....: ==> Parameters:
  3. ....: <== Total: 4
  4. ....: 总记录数:6
  5. ....: list:[UserInfoDO(id=1, userName=张三, age=22, createTime=2019-10-08T20:52:46), UserInfoDO(id=2, userName=李四, age=21, createTime=2019-12-23T20:22:54), UserInfoDO(id=3, userName=王二, age=22, createTime=2019-12-23T20:23:15), UserInfoDO(id=4, userName=马五, age=20, createTime=2019-12-23T20:23:15)]
  6. ....: result:PageResult{isFirstPage=true, isLastPage=false, pageNum=1, pageSize=4, totalSize=6, totalPage=2, data=[UserInfoDO(id=1, userName=张三, age=22, createTime=2019-10-08T20:52:46), UserInfoDO(id=2, userName=李四, age=21, createTime=2019-12-23T20:22:54), UserInfoDO(id=3, userName=王二, age=22, createTime=2019-12-23T20:23:15), UserInfoDO(id=4, userName=马五, age=20, createTime=2019-12-23T20:23:15)]}

通过日志分析,发现普通的SELECT * FROM user_info_pageable 被重新组装成SELECT * FROM user_info_pageable limit 4,说明拦截器实现的分页成功。

三、总结

两种方式:Pagehelper 分页和自己实现,根据实际情况自己选用

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/88264
推荐阅读
相关标签
  

闽ICP备14008679号