通用分页处理:从繁琐到简洁的转变

一、前言

在处理需要对整个表的数据进行处理或者数据同步的场景中,由于数据量通常较大,我们常常会采用不断地分页查询来进行操作。通常情况下,我们需要编写一个分页查询数据表的方法,例如下面的selectUserPageData方法,并且按照类似如下的逻辑来实现:

public void oldPageBatch() {
    int currentPage = 1;
    int pageCount = 1000;
    PageInfo<User> page = selectUserPageData(currentPage, pageCount);
    log.info("[PAGE-BATCH-USER]:pageNum: {},pageSize: {},totalData: {}", currentPage, pageCount, page.getTotal());
    doBatchHand(page.getList());
    // 处理后面数据
    boolean hasNextPage = page.isHasNextPage();
    for (int i = 2; i <= page.getPages(); i++) {
        PageInfo<User> tmpPage = selectUserPageData(i, pageCount);
        log.info("[PAGE-BATCH-USER]:pageNum:{}, pageSize:{}, totalData:{}", i, pageCount, tmpPage.getTotal());
        doBatchHand(tmpPage.getList());
    }
}

二、通用分页处理方法的实现

上面那样的实现方式虽然能够达成所需的功能,然而存在一定的繁琐性。而且,倘若后续遇到类似的场景,还需要重新编写一遍类似的代码。在多次遭遇此类场景之后,我开始思考如何构建一个通用的分页处理方法。那应该如何实现呢?

首先,在不同的业务中,上述代码的主体逻辑基本一致,唯有分页方法selectUserPageData和批量的逻辑处理方法doBatchHand在各个业务代码里有所不同。针对这种情况,我借助设计模式中的模板方法模式,提取出公共的逻辑,再由子类来实现各自特定的分页方法selectUserPageData和批量的逻辑处理方法doBatchHand

其次,对于分页操作,可以利用通用的PageHelper组件来进行简化。如此一来,子类无需再编写分页方法selectUserPageData,直接传入原有的非分页的查询方法即可。

实现的具体步骤如下:

一、引入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
     <version>1.2.10</version>
</dependency>

二、创建AbstractPageBatchService

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import java.util.List;

/**
 * @author fhey
 * @Date 2024/7/10 17:37
 * @Description 类注释
 */
@Slf4j
public abstract class AbstractPageBatchService<T> {
    private PageInfo<T> selectPageData(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize, true, false, false);
        return PageInfo.of(selectAll());
    }

    public void batchHand(int pageCount) {
        String className = this.getClass().getSimpleName();
        int currentPage = 1;
        PageInfo page = selectPageData(currentPage, pageCount);
        log.info("[PAGE - BATCH - {}]:pageNum: {},pageSize: {},totalData: {}",className, currentPage, pageCount, page.getTotal());
                doBatchHand(page.getList());
        // 处理后面数据
        boolean hasNextPage = page.isHasNextPage();
        for (int i = 0; i < page.getPages(); i++) {
            PageInfo tmpPage = selectPageData(i + 1, pageCount);
            log.info("[PAGE-BATCH-{}]:pageNum:{}, pageSize:{}, totalData:{}", className, i + 1, pageCount, tmpPage.getTotal());
            doBatchHand(tmpPage.getList());
         }

        while (hasNextPage) {
            ++currentPage;
            PageInfo tmpPage = selectPageData(currentPage, pageCount);
            hasNextPage = tmpPage.isHasNextPage();
            log.info("[PAGE-BATCH-{}]:pageNum:{}, pageSize:{}, totalData:{}", className, currentPage, pageCount, tmpPage.getTotal());
            doBatchHand(tmpPage.getList());
        }
    }
    abstract List<T> selectAll();
    abstract void doBatchHand(List<T> list);
}

如此一来,当需要进行分页处理时,只需继承AbstractPageBatchService,并实现全量查询方法selectAll和处理逻辑方法doBatchHand即可。

三、使用案例

下面以分页查询用户表,并批量输出用户id来展示使用的示例:

  1. UserPageBatchHand
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fhey.mapper.UserMapper;
import com.fhey.model.po.User;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;

/**
 * @author fhey
 * @Date 2024/7/10 17:37
 * @Description 类注释
 */
@Slf4j
@Component
public class UserPageBatchHand extends AbstractPageBatchService<User>{

    @Autowired
    UserMapper userMapper;

    @Override
    List<User> selectAll() {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.isNotNull(User::getId);
        List<User> userList = userMapper.selectList(wrapper);
        return userList;
    }
    
    //因为是测试,只把主键id打印出来
    @Override
    void doBatchHand(List<User> list) {
        System.out.println("UserBatchHand start");
        list.forEach(a-> System.out.println(a.getId()));
        System.out.println("UserBatchHand end");
    }
}
  1. UserController
package com.fhey.controller;

import com.fhey.model.po.User;
import com.fhey.service.pagebatch.UserPageBatchHand;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

/**
 * @author fhey
 * @date 2024/7/10 14:07
 * @details 详情
 */
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class UserController {

    private final UserPageBatchHand userPageBatchHand;

    @GetMapping("/test/userPageBatchHand")
    public void searchStuById() {
        userPageBatchHand.batchHand(10);
    }
}

由于数据量较多,这里仅展示第一次处理的日志,测试结果如下:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@46beaceb] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.jdbc.JDBC4Connection@73a332ec] will not be managed by Spring
==>  Preparing: SELECT count(0) FROM user WHERE deleted = 0 AND (id IS NOT NULL)
==> 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 AND (id IS NOT NULL) 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@46beaceb]
2024-08-06 16:12:13.599  INFO 18396 --- [nio-9000-exec-3] c.f.s.p.AbstractPageBatchService         : [PAGE - BATCH - UserPageBatchHand]:pageNum: 1,pageSize: 10,totalData: 2003
UserBatchHand start
1820471883522285569
1820472953300492290
1820493273394765825
1820497296638267393
1820497296638267394
1820497296638267395
1820497296638267396
1820497296638267397
1820497296638267398
1820497296638267399
UserBatchHand end

测试结果表明,成功实现了分页,第一次仅查出 10 条数据,并按处理逻辑打印出数据的id

四、总结

在处理大量数据的表操作或数据同步时,为应对数据量过大的情况,通常采用分页查询。最初的实现方式存在繁琐和重复编写的问题。经过多次类似场景的经历,通过运用模板方法模式和通用的PageHelper组件,构建了更高效的通用分页处理方法此通用分页处理方法提高了代码的复用性和可维护性,减少了重复编写的工作量。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

知北游z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值