一个注解实现自动分页

一、前言

在我们的业务系统中,常常会涉及到查询列表的操作,而运用分页功能能够以一种灵活且清晰的方式展示数据结果,其效果犹如翻阅书籍,为用户带来出色的阅读体验。
然而,传统的分页方式往往需要开发者在代码中自行进行分页处理。考虑到分页的逻辑通常较为固定,且与业务逻辑关联不大。基于此,我们萌生了使用自定义注解,并结合切面技术以及老牌的分页插件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功能。

四、结语

通过以上对自动分页注解的详尽阐释、实现流程以及使用实例的展现,我们成功构建了一种高效、便捷且实现了解耦的分页解决办法。使用自动分页注解,仅需添加一个注解就能完成分页操作,无需在代码中重复进行繁琐的分页处理。这不但精简了代码,提升了效率,增强了代码的可维护性与可读性,还为业务系统中的数据查询和展示赋予了更优质的用户体验。坚信在未来的开发进程中,如此的设计会为我们带来更多的便捷与优势,持续优化系统的性能与功能。期望这一技术在实际运用中能够充分彰显其巨大价值,为业务的蓬勃发展给予强有力的支撑。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知北游z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值