文章目录
一、前言
在我们的业务系统中,常常会涉及到查询列表的操作,而运用分页功能能够以一种灵活且清晰的方式展示数据结果,其效果犹如翻阅书籍,为用户带来出色的阅读体验。
然而,传统的分页方式往往需要开发者在代码中自行进行分页处理。考虑到分页的逻辑通常较为固定,且与业务逻辑关联不大。基于此,我们萌生了使用自定义注解,并结合切面技术以及老牌的分页插件pagehelper来实现通用分页注解的想法。如此一来,我们仅需添加一个注解,即可优雅地实现分页功能,无需在代码中重复进行分页操作,并且成功地将分页与业务逻辑解耦。依照这一思路,我成功地完成了自动分页注解的开发。以下是具体的实现代码:
2、添加通用对象类
2.1 添加通用返回对象类RespVo
package com.fhey.base.vo.resp;
/**
* @author: fhey
* @Date: 2019/8/12 15:59
* @Description: 通用返回对象
*/
@Data
public class RespVo<T> {
public static final String SUCCESS_CODE = "0000";
public static final String SUCCESS_MSG = "操作成功";
public static final String FAIL_CODE = "0911";
public static final String FAIL_MSG = "系统正忙,请稍后重试";
private String resCode;
private String resMsg;
private T result;
public RespVo() {
}
public RespVo(String resCode, String resMsg, T result) {
this.resCode = resCode;
this.resMsg = resMsg;
this.result = result;
}
public static RespVo success() {
return CUSTOM(SUCCESS_CODE, SUCCESS_MSG, null);
}
public static <T> RespVo<T> success(T obj) {
return CUSTOM(SUCCESS_CODE, SUCCESS_MSG, obj);
}
public static RespVo fail() {
return CUSTOM(FAIL_CODE, FAIL_MSG, null);
}
}
2.2 添加通用分页请求对象类PageQueryReq
package com.fhey.base.vo.req;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageQueryReq {
/**
* 当前页码
*/
private Integer pageNum = 1;
/**
* 每页数量
*/
private Integer pageSize = 10;
}
3、定义自动分页注解 @AutoPage
package com.fhey.annotation;
import java.lang.annotation.*;
/**
* @author: fhey
* @create: 2024-08-06 17:39
* @description: 自动分页注解
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoPage {
}
4、自动分页注解切面AutoPageAspect
package com.fhey.aspect;
import com.fhey.base.vo.req.PageQueryReq;
import com.fhey.base.vo.resp.RespVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author: fhey
* @create: 2024-08-06 17:42
* @description: 自动分页注解切面
*/
@Slf4j
@Aspect
@Component
public class AutoPageAspect {
private static final String PAGE_NUM_NAME = "pageNum";
private static final String PAGE_SIZE_NAME = "pageSize";
@Around("@annotation(com.fhey.annotation.AutoPage)")
public Object autoPage(ProceedingJoinPoint joinPoint) throws Throwable {
try {
Object[] args = joinPoint.getArgs();
PageQueryReq pageQueryReq = getPageQueryReq(joinPoint);
PageHelper.startPage(pageQueryReq.getPageNum(), pageQueryReq.getPageSize());
Object result = joinPoint.proceed(args);
return getPageResult(result);
} catch (Exception e) {
log.error("Error during paging query", e);
throw e; // 或者根据业务需求返回一个错误响应
}
}
/**
* 获取分页参数
*
* @param joinPoint
* @return
*/
public PageQueryReq getPageQueryReq(ProceedingJoinPoint joinPoint) {
int pageNum = 1;
int pageSize = 10;
String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < paramNames.length; i++) {
String paramName = paramNames[i];
if (PAGE_NUM_NAME.equals(paramName)) {
pageNum = validateAndConvertToInt(args[i], 1);
} else if (PAGE_SIZE_NAME.equals(paramName)) {
pageSize = validateAndConvertToInt(args[i], 10);
}
}
return new PageQueryReq(pageNum, pageSize);
}
private int validateAndConvertToInt(Object value, int defaultValue) {
if (value instanceof Integer) {
return (Integer) value;
} else if (value instanceof String) {
try {
return Integer.parseInt((String) value);
} catch (NumberFormatException e) {
log.warn("Invalid number format for parameter, using default value: {}", defaultValue);
}
}
return defaultValue;
}
/**
* 对分页结果进行包装 如果分页成功则会返回PageResult
*
* @param result
*/
private Object getPageResult(Object result) {
if (result instanceof RespVo) {
RespVo respVo = (RespVo) result;
Object data = respVo.getResult();
if (data instanceof List) {
PageInfo pageInfo = PageInfo.of((List) data);
return RespVo.success(pageInfo);
} else {
return result;
}
} else if (result instanceof List) {
return PageInfo.of((List) result);
}
return result;
}
}
三、使用示例
1、验证代码
在原有的全量查询方法上直接添加@AutoPage注解,示例代码如下:
@PostMapping("/testAutoPage")
@AutoPage
public RespVo testAutoPage(@RequestBody UserPageQueryReq req) {
LambdaQueryChainWrapper<User> lambdaQuery = userService.lambdaQuery();
if (StringUtils.isNotBlank(req.getUserId())){
lambdaQuery.eq(User::getId, req.getUserId());
}
if (StringUtils.isNotBlank(req.getUserName())){
lambdaQuery.eq(User::getUserName, req.getUserName());
}
List<User> userList = lambdaQuery.list();
return RespVo.success(userList);
}
2、测试结果
通过 postman 进行请求验证:
返回结果如下:
{
"resCode": "0000",
"resMsg": "操作成功",
"result": {
"total": 2003,
"list": [
{
"id": "1820471883522285569",
"createTime": "2024-08-05 22:48:14",
"createUser": "testUser",
"updateTime": "2024-08-05 22:48:14",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明139"
},
{
"id": "1820472953300492290",
"createTime": "2024-08-05 22:52:29",
"createUser": "testUser",
"updateTime": "2024-08-05 22:52:29",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明509"
},
{
"id": "1820493273394765825",
"createTime": "2024-08-06 00:13:14",
"createUser": "testUser",
"updateTime": "2024-08-06 00:13:14",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明267"
},
{
"id": "1820497296638267393",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明7959"
},
{
"id": "1820497296638267394",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明7682"
},
{
"id": "1820497296638267395",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明2768"
},
{
"id": "1820497296638267396",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明4420"
},
{
"id": "1820497296638267397",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明4523"
},
{
"id": "1820497296638267398",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明1434"
},
{
"id": "1820497296638267399",
"createTime": "2024-08-06 00:29:13",
"createUser": "testUser",
"updateTime": "2024-08-06 00:29:13",
"updateUser": "testUser",
"deleted": 0,
"userName": "小明6403"
}
],
"pageNum": 1,
"pageSize": 10,
"size": 10,
"startRow": 1,
"endRow": 10,
"pages": 201,
"prePage": 0,
"nextPage": 2,
"isFirstPage": true,
"isLastPage": false,
"hasPreviousPage": false,
"hasNextPage": true,
"navigatePages": 8,
"navigatepageNums": [
1,
2,
3,
4,
5,
6,
7,
8
],
"navigateFirstPage": 1,
"navigateLastPage": 8
}
}
可以看到结果只返回了 10 条数据,并且已经被分页对象所包裹。再查看系统日志的 SQL 日志:
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@166d984f] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@7d1d049c] will not be managed by Spring
==> Preparing: SELECT count(0) FROM user WHERE deleted = 0
==> Parameters:
<== Columns: count(0)
<== Row: 2003
<== Total: 1
==> Preparing: SELECT id,user_name,create_time,create_user,update_time,update_user,deleted FROM user WHERE deleted=0 LIMIT ?
==> Parameters: 10(Integer)
<== Columns: id, user_name, create_time, create_user, update_time, update_user, deleted
<== Row: 1820471883522285569, 小明139, 2024-08-05 22:48:14.0, testUser, 2024-08-05 22:48:14.0, testUser, 0
<== Row: 1820472953300492290, 小明509, 2024-08-05 22:52:29.0, testUser, 2024-08-05 22:52:29.0, testUser, 0
<== Row: 1820493273394765825, 小明267, 2024-08-06 00:13:14.0, testUser, 2024-08-06 00:13:14.0, testUser, 0
<== Row: 1820497296638267393, 小明7959, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267394, 小明7682, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267395, 小明2768, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267396, 小明4420, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267397, 小明4523, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267398, 小明1434, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Row: 1820497296638267399, 小明6403, 2024-08-06 00:29:13.0, testUser, 2024-08-06 00:29:13.0, testUser, 0
<== Total: 10
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@166d984f]
也可以看到SQL里已经被加入了LIMIT 10,这说明自动分页注解成功实现了自动分页devil功能。
四、结语
通过以上对自动分页注解的详尽阐释、实现流程以及使用实例的展现,我们成功构建了一种高效、便捷且实现了解耦的分页解决办法。使用自动分页注解,仅需添加一个注解就能完成分页操作,无需在代码中重复进行繁琐的分页处理。这不但精简了代码,提升了效率,增强了代码的可维护性与可读性,还为业务系统中的数据查询和展示赋予了更优质的用户体验。坚信在未来的开发进程中,如此的设计会为我们带来更多的便捷与优势,持续优化系统的性能与功能。期望这一技术在实际运用中能够充分彰显其巨大价值,为业务的蓬勃发展给予强有力的支撑。