项目地址:https://github.com/Gang-bb/Study-Record/tree/main/Gangbb-Vue
本章结束后对应的节选代码文件:Gangbb-Vue-09-PageHelper
历史遗留TODO:
- 第四章
- 登录日志还未实现。(到登录和权限模块完成)
LogAspect
从缓存获取当前的用户信息使用模拟的数据(到登录和权限模块完成)本章将留下TODO:
- 无
本章将解决TODO:
- 无
本来这章应该跟Mybatis一起的,给落下了,现在补回来
在第三章中已经在Gangbb-Vue
引入了分页的依赖。
<dependencies>
<dependency>
<groupId>com.github.pagehelpergroupId>
<artifactId>pagehelper-spring-boot-starterartifactId>
dependency>
dependencies>
# PageHelper分页插件
pagehelper:
# 指定分页插件使用哪种数据库方言
helperDialect: mysql
# 当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。
reasonable: true
# 支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。
supportMethodsArguments: true
这里简单配置了几项。
所有配置项:
helperDialect
:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。 你可以配置helperDialect
属性来指定分页插件使用哪种方言。配置时,可以使用下面的缩写值:
oracle
,mysql
,mariadb
,sqlite
,hsqldb
,postgresql
,db2
,sqlserver
,informix
,h2
,sqlserver2012
,derby
**特别注意:**使用 SqlServer2012 数据库时,需要手动指定为sqlserver2012
,否则会使用 SqlServer2005 的方式进行分页。
你也可以实现AbstractHelperDialect
,然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。offsetAsPageNum
:默认值为false
,该参数对使用RowBounds
作为分页参数时有效。 当该参数设置为true
时,会将RowBounds
中的offset
参数当成pageNum
使用,可以用页码和页面大小两个参数进行分页。rowBoundsWithCount
:默认值为false
,该参数对使用RowBounds
作为分页参数时有效。 当该参数设置为true
时,使用RowBounds
分页会进行 count 查询。pageSizeZero
:默认值为false
,当该参数设置为true
时,如果pageSize=0
或者RowBounds.limit = 0
就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page
类型)。reasonable
:分页合理化参数,默认值为false
。当该参数设置为true
时,pageNum<=0
时会查询第一页,pageNum>pages
(超过总数时),会查询最后一页。默认false
时,直接根据参数进行查询。params
:为了支持startPage(Object params)
方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置pageNum,pageSize,count,pageSizeZero,reasonable
,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
。supportMethodsArguments
:支持通过 Mapper 接口参数来传递分页参数,默认值false
,分页插件会从查询方法的参数值中,自动根据上面params
配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的com.github.pagehelper.test.basic
包下的ArgumentsMapTest
和ArgumentsObjTest
。autoRuntimeDialect
:默认值为false
。设置为true
时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012
,只能使用sqlserver
),用法和注意事项参考下面的场景五。closeConn
:默认值为true
。当使用运行时动态数据源或没有设置helperDialect
属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true
关闭,设置为false
后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。aggregateFunctions
(5.1.5+):默认为所有常见数据库的聚合函数,允许手动添加聚合函数(影响行数),所有以聚合函数开头的函数,在进行 count 转换时,会套一层。其他函数和列会被替换为 count(0),其中count列可以自己配置。
更多信息可以可以看官方文档:
https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
这是一个web层通用处理类。核心方法startPage()
请求分页数据方法,使继承BaseController
的Controller可以快速使用分页。
public class BaseController {
protected final Logger logger = LoggerFactory.getLogger(BaseController.class);
/**
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
*/
@InitBinder
public void initBinder(WebDataBinder binder)
{
// Date 类型转换
binder.registerCustomEditor(Date.class, new PropertyEditorSupport()
{
@Override
public void setAsText(String text)
{
setValue(DateUtils.parseDate(text));
}
});
}
/**
* 设置请求分页数据
*/
protected void startPage()
{
//从前端获取分页参数
MyPage pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize))
{
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
PageHelper.startPage(pageNum, pageSize, orderBy);
}
}
/**
* 响应请求分页数据
*/
@SuppressWarnings({
"rawtypes", "unchecked" })
protected TableDataInfo getDataTable(List<?> list)
{
TableDataInfo rspData = new TableDataInfo();
rspData.setCode(HttpStatus.SUCCESS);
rspData.setMsg("查询成功");
rspData.setRows(list);
rspData.setTotal(new PageInfo(list).getTotal());
return rspData;
}
/**
* 响应返回结果
*
* @param rows 影响行数
* @return 操作结果
*/
protected ApiRestResponse toApiRes(int rows)
{
return rows > 0 ? ApiRestResponse.success() : ApiRestResponse.error();
}
/**
* 页面跳转
*/
public String redirect(String url)
{
return StringUtils.format("redirect:{}", url);
}
}
若依封装的自定义分页结果。只有4个参数,感觉不太够。还是Pagehelper
提供的PageInfo
类返回的信息比较全。
对比一下便知:
若依TableDataInfo
返回的信息:
{
"total": 5,
"rows": [
{
"creator": "",
"createTime": "2021-03-10 15:04:32",
"reviser": "",
"updateTime": null,
"isDel": 0,
"remark": "",
"id": 1160,
"title": "测试系统自定义异常",
"businessType": 0,
"businessTypes": null,
"classMethod": "com.gangbb.test.controller.TestExceptionController.testApiException()",
"requestMethod": "GET",
"operatorType": 1,
"operationName": "Gangbb",
"operationUrl": "/test/exception/e",
"operationIp": "127.0.0.1",
"operationLocation": "内网IP",
"operationParam": "{}",
"jsonResult": "null",
"operationStatus": 1,
"errorMsg": "System internal errors",
"operationTime": "2021-03-10 15:04:32"
},
{
"creator": "",
"createTime": "2021-03-10 15:05:43",
"reviser": "",
"updateTime": null,
"isDel": 0,
"remark": "",
"id": 1161,
"title": "测试success",
"businessType": 0,
"businessTypes": null,
"classMethod": "com.gangbb.test.controller.TestExceptionController.testSuccess()",
"requestMethod": "POST",
"operatorType": 1,
"operationName": "Gangbb",
"operationUrl": "/test/exception/success",
"operationIp": "127.0.0.1",
"operationLocation": "内网IP",
"operationParam": "",
"jsonResult": "{\"code\":\"20000\",\"msg\":\"操作成功(success)\",\"data\":{\"info\":\"Test\",\"name\":\"LLL\"}}",
"operationStatus": 0,
"errorMsg": null,
"operationTime": "2021-03-10 15:05:43"
}
],
"code": 200,
"msg": "查询成功"
}
TableDataInfo
类代码自行看项目文件,不再贴出。
PageInfo
返回的信息:
{
"total": 5,
"list": [
{
"creator": "",
"createTime": "2021-03-10 15:04:32",
"reviser": "",
"updateTime": null,
"isDel": 0,
"remark": "",
"id": 1160,
"title": "测试系统自定义异常",
"businessType": 0,
"businessTypes": null,
"classMethod": "com.gangbb.test.controller.TestExceptionController.testApiException()",
"requestMethod": "GET",
"operatorType": 1,
"operationName": "Gangbb",
"operationUrl": "/test/exception/e",
"operationIp": "127.0.0.1",
"operationLocation": "内网IP",
"operationParam": "{}",
"jsonResult": "null",
"operationStatus": 1,
"errorMsg": "System internal errors",
"operationTime": "2021-03-10 15:04:32"
},
{
"creator": "",
"createTime": "2021-03-10 15:05:43",
"reviser": "",
"updateTime": null,
"isDel": 0,
"remark": "",
"id": 1161,
"title": "测试success",
"businessType": 0,
"businessTypes": null,
"classMethod": "com.gangbb.test.controller.TestExceptionController.testSuccess()",
"requestMethod": "POST",
"operatorType": 1,
"operationName": "Gangbb",
"operationUrl": "/test/exception/success",
"operationIp": "127.0.0.1",
"operationLocation": "内网IP",
"operationParam": "",
"jsonResult": "{\"code\":\"20000\",\"msg\":\"操作成功(success)\",\"data\":{\"info\":\"Test\",\"name\":\"LLL\"}}",
"operationStatus": 0,
"errorMsg": null,
"operationTime": "2021-03-10 15:05:43"
}
],
"pageNum": 1,
"pageSize": 2,
"size": 2,
"startRow": 1,
"endRow": 2,
"pages": 3,
"prePage": 0,
"nextPage": 2,
"isFirstPage": true,
"isLastPage": false,
"hasPreviousPage": false,
"hasNextPage": true,
"navigatePages": 8,
"navigatepageNums": [
1,
2,
3
],
"navigateFirstPage": 1,
"navigateLastPage": 3
}
若依源码中叫PageDomain
类
主要的属性:
/** 当前记录起始索引 */
private Integer pageNum;
/** 每页显示记录数 */
private Integer pageSize;
/** 排序列 */
private String orderByColumn;
/** 排序的方向desc或者asc */
private String isAsc = "asc";
这是前端调用接口时,可选择传入的参数。
而参数传递的声明,没有写在各个使用分页方法的参数列表中。直接通过TableSupport
从客户端工具类ServletUtils
中获取分页请求的各项可填参数。
但是若依如此设计,实测没有进行参数校验。例如:
pageNum
传入一个字母,这时返回的结果是全部数据也未分页,对于前端是不利于排查的。
使用时可以增加一些适当的校验。例如:
(只是举例,实际应用应该更严谨一点)
校验:
本章在全局错误处理GlobalExceptionHandler
增加了对MethodArgumentTypeMismatchException
参数转换异常的处理。
/**
* MethodArgumentTypeMismatchException 参数类型转换异常
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ApiRestResponse resolveMethodArgumentNotValidException(MethodArgumentTypeMismatchException ex){
String msg = "参数<" + ex.getName() + ">校验失败:" + ex.getMessage();
return ApiRestResponse.error("A0003", msg);
}
DateUtils
:时间工具类
TableSupport
:获取分页参数
SqlUtil
:sql操作工具类
(代码自行到项目中复制)