为何访问/categories端点返回全量分类而非分页结果?
分类列表分页异常问题
调用带pageNum参数的/categories/page/{pageNum}端点并传入关键词时,客户端可返回分页分类列表;但调用/categories端点时,返回所有分类结果而非分页数据。
控制器代码
@GetMapping("/categories") @PreAuthorize("hasAnyAuthority('Admin', 'Editor')") public String listFirstPage(@Param("sortDir") String sortDir, Model model) { if(sortDir == null) sortDir = "asc"; return listByPage(1, "name", null, model); } @GetMapping("/categories/page/{pageNum}") public String listByPage(@PathVariable("pageNum")int pageNum, @Param("sortDir") String sortDir, @Param("keyword") String keyword, Model model) { if (sortDir == null || sortDir.isEmpty()) { sortDir = "asc"; } CategoryPageInfo pageInfo = new CategoryPageInfo(); List<Category> listByPage = service.listByPage(pageInfo, pageNum, sortDir, model, keyword); long startCount = (pageNum - 1) * CategoryService.ROOT_CATEGORY_PER_PAGE + 1; long endCount = startCount + CategoryService.ROOT_CATEGORY_PER_PAGE -1; if(endCount > pageInfo.getTotalElement()) endCount = pageInfo.getTotalElement(); String reverseSortDir = sortDir.equals("asc") ? "desc" : "asc"; model.addAttribute("totalPages", pageInfo.getTotalPages()); model.addAttribute("totalElements", pageInfo.getTotalElement()); model.addAttribute("currentPage", pageNum); model.addAttribute("pageNum", pageNum); model.addAttribute("sortField", "name"); model.addAttribute("sortDir", sortDir); model.addAttribute("keyword", keyword); model.addAttribute("categories", listByPage); model.addAttribute("startCount", startCount); model.addAttribute("endCount", endCount); model.addAttribute("reverseSortDir", reverseSortDir); return "categories/categories.html"; }
Service层代码
package com.shopme.admin.category; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.springframework.beans.factory.annotation.Autowired; 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.stereotype.Service; import org.springframework.ui.Model; import com.shopme.admin.FileUploadUtil; import com.shopme.common.entity.Category; import jakarta.transaction.Transactional; @Service @Transactional public class CategoryService { public static final int ROOT_CATEGORY_PER_PAGE = 4; @Autowired private CategoryRepository repo; public List<Category> listByPage(CategoryPageInfo pageInfo, int pageNum, String sortDir, Model model, String keyword) { Sort sort = Sort.by("name"); if (sortDir.equals("asc")) { sort = sort.ascending(); } else if (sortDir.equals("desc")) { sort = sort.descending(); } Pageable pageable = PageRequest.of(pageNum - 1, ROOT_CATEGORY_PER_PAGE, sort); Page<Category> categoriesPage = null; if (keyword != null && !keyword.isEmpty()) { categoriesPage = repo.search(keyword, pageable); } else { categoriesPage = repo.findRootCategoriesPage(pageable); } List<Category> rootCategories = categoriesPage.getContent(); pageInfo.setTotalElement(categoriesPage.getTotalElements()); pageInfo.setTotalPages(categoriesPage.getTotalPages()); if (keyword != null && !keyword.isEmpty()) { List<Category> searchResult = categoriesPage.getContent(); for(Category category : searchResult) { category.setHasChildren(category.getChildren().size() > 0); } return searchResult; } else { return listHierarchicalCategories(rootCategories, sortDir); } } private List<Category> listHierarchicalCategories(List<Category> rootCategories, String sortDir) { List<Category> listHierarchicalCategories = new ArrayList<>(); for (Category rootCategory : rootCategories) { listHierarchicalCategories.add(Category.copyFull(rootCategory)); Set<Category> children = sortSubCategory(rootCategory.getChildren(), sortDir); for (Category subCategory : children) { String name = "--" + subCategory.getName(); listHierarchicalCategories.add(Category.copyFull(subCategory, name)); listSubHierarchicalCategories(listHierarchicalCategories, subCategory, 1, sortDir); } } return listHierarchicalCategories; } private void listSubHierarchicalCategories(List<Category> listHierarchicalCategories, Category parent, int subLevel, String sortDir) { Set<Category> children = sortSubCategory(parent.getChildren(), sortDir); int newSubLevel = subLevel + 1; for (Category subCategory : children) { StringBuilder name = new StringBuilder(); for (int i = 0; i < newSubLevel; i++) { name.append("--"); } name.append(subCategory.getName()); listHierarchicalCategories.add(Category.copyFull(subCategory, name.toString())); listSubHierarchicalCategories(listHierarchicalCategories, subCategory, newSubLevel, sortDir); } } }
问题分析与解决方案
你的判断准确,问题出在非关键词搜索场景下,分页仅限制了根分类的数量,但递归添加的所有子分类未受分页控制,导致最终返回的列表包含当前页根分类下的所有层级子分类,总数量远超设定的分页条数。
核心逻辑说明
当前代码的分页逻辑是针对根分类设计的:
repo.findRootCategoriesPage(pageable)会按分页参数查询指定数量的根分类- 后续的层级递归是为了展示分类的树形结构,子分类属于根分类的附属内容,不需要分页限制
调整方案
- 确认分页统计逻辑:
pageInfo中的totalPages和totalElements是基于根分类总数计算的,前端页面的分页按钮、条数统计需基于这些值展示,而非返回的所有分类(根+子)的数量。 - 保持层级展示逻辑:不需要修改递归添加子分类的代码,因为分类管理页面通常是按根分类分页,子分类随根分类展开展示,这是合理的交互逻辑。
- 如果需求是全部分类分页:若要将所有分类(含子分类)扁平化后分页,需重构Service逻辑——先将所有分类扁平化,再对扁平化后的列表进行分页,但这种方式会丢失分类层级结构,一般不推荐用于分类管理场景。
内容的提问来源于stack exchange,提问作者Antonio Della Luce
相关产品推荐
相关产品推荐

