分页场景下如何对全量记录执行数据筛选(Spring Boot+Postgres+React)
实现全量筛选分页的方案
核心逻辑是将筛选参数和分页参数共同传给后端,后端先执行全量数据筛选,再对筛选后的结果集做分页计算,避免先分页后筛选导致只能过滤当前页数据的问题。
后端(Spring Boot + Postgres)实现
1. 统一参数接收
新增参数接收类,同时承载分页参数和筛选参数:
import lombok.Data; @Data public class MessageQueryReq { // 分页参数 private Integer pageNumber = 1; private Integer pageSize = 10; // 筛选参数,可根据业务新增 private String status; private String acknowledgeMsg; }
2. 动态条件查询+分页
不管用JPA、MyBatis还是原生SQL,都要保证执行顺序:先执行where条件过滤全量数据,再做分页计算。以Spring Data JPA + Specification动态查询为例:
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import jakarta.persistence.criteria.Predicate; import cn.hutool.core.util.StrUtil; public Page<MessageDTO> queryMessageList(MessageQueryReq req) { // 构造动态筛选条件 Specification<Message> spec = (root, query, cb) -> { List<Predicate> predicates = new ArrayList<>(); // 状态筛选不为空时添加条件 if (StrUtil.isNotBlank(req.getStatus())) { predicates.add(cb.equal(root.get("status"), req.getStatus())); } // 回执内容模糊筛选 if (StrUtil.isNotBlank(req.getAcknowledgeMsg())) { predicates.add(cb.like(root.get("acknowledgeMsg"), "%" + req.getAcknowledgeMsg() + "%")); } return cb.and(predicates.toArray(new Predicate[0])); }; // 构造分页参数:JPA默认页码从0开始,和前端约定好偏移规则 Pageable pageable = PageRequest.of( req.getPageNumber() - 1, req.getPageSize(), Sort.by(Sort.Direction.DESC, "msg_id") ); // JPA会先执行count查询计算符合条件的总条数,再查询当前页数据 Page<Message> messagePage = messageRepository.findAll(spec, pageable); // 转换为DTO,封装为你要求的返回格式即可,totalRecords取messagePage.getTotalElements(),numPages取messagePage.getTotalPages() }
如果用原生Postgres SQL,写法参考:
-- 先查符合条件的总条数 SELECT count(*) FROM message WHERE status = ? AND acknowledge_msg LIKE ?; -- 再查当前页数据 SELECT * FROM message WHERE status = ? AND acknowledge_msg LIKE ? ORDER BY msg_id DESC LIMIT ? OFFSET ?;
优化建议
对高频筛选的字段添加索引,降低全表扫描的性能损耗:
CREATE INDEX idx_message_status ON message(status);
前端(React)实现
1. 状态管理新增筛选变量
import { useState } from 'react'; import axios from 'axios'; const MessageList = () => { // 分页状态 const [pageNumber, setPageNumber] = useState(1); const [pageSize, setPageSize] = useState(10); // 筛选状态 const [filters, setFilters] = useState({ status: '', acknowledgeMsg: '' }); // 列表数据状态 const [list, setList] = useState([]); const [total, setTotal] = useState(0);
2. 统一请求逻辑
不管是切换分页还是修改筛选条件,都要携带当前所有筛选参数请求接口,修改筛选条件时默认重置到第一页:
// 统一请求方法 const fetchList = async (currentPage, currentSize, currentFilters) => { const res = await axios.get('/api/message/list', { params: { pageNumber: currentPage, pageSize: currentSize, ...currentFilters } }); const { dtoList, totalRecords } = res.data.messages; setList(dtoList); setTotal(totalRecords); }; // 筛选条件变更触发 const handleFilterUpdate = (newFilters) => { setFilters(newFilters); // 重置到第一页 setPageNumber(1); fetchList(1, pageSize, newFilters); }; // 分页切换触发 const handlePageChange = (newPage) => { setPageNumber(newPage); fetchList(newPage, pageSize, filters); };
这样每次请求的都是筛选后全量数据的对应分页,完全符合需求。
内容的提问来源于stack exchange,提问作者rudeTool
相关产品推荐
相关产品推荐

