关于spring boot中使用PageHelper类
首先引入依赖
com.github.pagehelper pagehelper-spring-boot-starter 1.2.3
PageHelper使用时比较简单的,调用startPage方法,第一个参数:当前页数,第二个参数:每页查询展示记录数
PageHelper.startPage(pageNum,pageSize);
但是为了更好的理解和使用,我去看了下源码,看了下大概是如何实现分页的,话不多说上代码
测试代码
/** * 通过PageHelper实现分页 * @param page * @param animal * @return */ @Override public PageHelperCustom
queryAnimalByPageHelper(PageHelperCustom page, Animal animal) { PageHelper.startPage(page.getPageNum(),page.getPageSize()); List list = animalMapperCustom.queryAnimalAndTypeByPageHelperList(animal); long count = new PageInfo<>(list).getTotal();//获取总数 page.setCount(count); page.setList(list); /*Page page1 = (Page)list; 这种方式也可以直接得到 count总数 System.out.println("page1>>>>>>>>>>"+page1.getTotal());*/ return page; }
mapper中sql,就是简单的查询,这里并没有做什么分页
打断点,当执行 List
MapperProxy代理类的invoke方法
public class PageInterceptor implements Interceptor { .... public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } if (this.isDefaultMethod(method)) { return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); }
继续往下执行进入excute
public Object execute(SqlSession sqlSession, Object[] args) { Object param; Object result; switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }
这里是执行select并且是查询多结果executeForMany
继续执行,中间仍有许多过程,直接看到最后执行查询的地方PageInterceptor,我们可以看到PageInterceptor 实现了Interceptor 拦截器的,到这里差不多就知道分页的基本原理了,最后是通过拦截器实现的,当然中间还有一些处理比如边界的设置等
public class PageInterceptor implements Interceptor {
......
public Object intercept(Invocation invocation) throws Throwable { Object var22; try { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement)args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds)args[2]; ResultHandler resultHandler = (ResultHandler)args[3]; Executor executor = (Executor)invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; if (args.length == 4) { boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { cacheKey = (CacheKey)args[4]; boundSql = (BoundSql)args[5]; } List resultList; if (this.dialect.skip(ms, parameter, rowBounds)) { resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } else { String msId = ms.getId(); Configuration configuration = ms.getConfiguration(); Map
additionalParameters = (Map)this.additionalParametersField.get(boundSql); if (this.dialect.beforeCount(ms, parameter, rowBounds)) { String countMsId = msId + this.countSuffix; MappedStatement countMs = this.getExistedMappedStatement(configuration, countMsId); Long count; if (countMs != null) { count = this.executeManualCount(executor, countMs, parameter, boundSql, resultHandler); } else { countMs = (MappedStatement)this.msCountMap.get(countMsId); if (countMs == null) { countMs = MSUtils.newCountMappedStatement(ms, countMsId); this.msCountMap.put(countMsId, countMs); } count = this.executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler); } if (!this.dialect.afterCount(count, parameter, rowBounds)) { Object var24 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds); return var24; } } if (!this.dialect.beforePage(ms, parameter, rowBounds)) { resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql); } else { parameter = this.dialect.processParameterObject(ms, parameter, boundSql, cacheKey); String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey); BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter); Iterator var17 = additionalParameters.keySet().iterator(); while(true) { if (!var17.hasNext()) { resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql); break; } String key = (String)var17.next(); pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key)); } } } var22 = this.dialect.afterPage(resultList, parameter, rowBounds); } finally { this.dialect.afterAll(); } return var22; }
执行完 intercept可以看到控制台有打印sql,这里用的数据库是mysql,所以用的LIMIT 分页,起始也是通过了数据源判断之后,对sql进行了相应的拼接
select t.*,atype.type_text as typeText from animal t join animal_type atype on atype.id = t.type where 1=1 LIMIT ?, ?
public String getPageSql(String sql, Page page, CacheKey pageKey) { StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14); sqlBuilder.append(sql); if (page.getStartRow() == 0) { sqlBuilder.append(" LIMIT ? "); } else { sqlBuilder.append(" LIMIT ?, ? "); } pageKey.update(page.getPageSize()); return sqlBuilder.toString(); }
如果是Oracle
public String getPageSql(String sql, Page page, CacheKey pageKey) { StringBuilder sqlBuilder = new StringBuilder(sql.length() + 120); if (page.getStartRow() > 0) { sqlBuilder.append("SELECT * FROM ( "); } if (page.getEndRow() > 0) { sqlBuilder.append(" SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM ( "); } sqlBuilder.append(sql); if (page.getEndRow() > 0) { sqlBuilder.append(" ) TMP_PAGE WHERE ROWNUM <= ? "); } if (page.getStartRow() > 0) { sqlBuilder.append(" ) WHERE ROW_ID > ? "); } return sqlBuilder.toString(); }
不同的数据库,分页都有不同的拼接实现
最后上面还可以获取查询的记录总数,如若不想查询可以这样设置
PageHelper.startPage(pageNum,pageSize,false);
public static
Page startPage(int pageNum, int pageSize) { return startPage(pageNum, pageSize, true); } public static Page startPage(int pageNum, int pageSize, boolean count) { return startPage(pageNum, pageSize, count, (Boolean)null, (Boolean)null); }
总之PageHelper作为分页工具是很不错的,支持多种数据库