Spring Boot分页异常求助:空表页码错误与数据展示混乱
Spring Boot分页功能问题排查与修复
问题描述
- 数据表为空时,分页结果错误显示1页和0页,预期应显示1页
- 分页数据展示混乱:访问第2页内容为空,返回第1页却显示本该属于第2页的元素
相关代码
HunterDAO.java
public interface HunterDAO { Page<HunterEntity> indexPagination(int page, int size); }
HunterService.java
@PersistenceContext private EntityManager entityManager; public HunterService(EntityManager theEntityManager) { this.entityManager = theEntityManager; } @Override public Page<HunterEntity> indexPagination(int page, int size) { TypedQuery<HunterEntity> query = entityManager.createQuery("SELECT h FROM HunterEntity h " + "INNER JOIN FETCH h.type_hunter_id th " + "INNER JOIN FETCH h.type_nen_id tn " + "INNER JOIN FETCH h.type_blood_id ts " + "WHERE h.deleted_at IS NULL " + "ORDER BY h.id ASC", HunterEntity.class); long totalCount = entityManager.createQuery("SELECT COUNT(h) FROM HunterEntity h WHERE h.deleted_at IS NULL", Long.class).getSingleResult(); query.setFirstResult(page * size); query.setMaxResults(size); List<HunterEntity> hunters = query.getResultList(); return new PageImpl<>(hunters, PageRequest.of(page, size), totalCount); }
HunterController.java
@GetMapping("/list") public String listHunters(Model model, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) { Page<HunterEntity> hunterPage = hunterService.indexPagination(page, size); model.addAttribute("hunters", hunterPage.getContent()); model.addAttribute("currentPage", hunterPage.getNumber()); model.addAttribute("totalPages", hunterPage.getTotalPages()); return "/hunter/list-hunters"; }
view.html
<div class="pagination justify-content-center"> <ul class="pagination"> <li class="page-item" th:each="i: ${#numbers.sequence(1, totalPages)}" th:class="${currentPage == i} ? 'active' : ''"> <a class="page-link" th:href="@{'/hunters/list?page=' + ${i}}">[[${i}]]</a> </li> </ul> </div>
问题修复方案
1. 空数据表时页码显示错误
当totalCount为0时,PageImpl的getTotalPages()默认返回0,但业务上预期空表时显示1页。可以重写getTotalPages()方法修正:
@Override public Page<HunterEntity> indexPagination(int page, int size) { // 原有查询逻辑保持不变 TypedQuery<HunterEntity> query = entityManager.createQuery("SELECT h FROM HunterEntity h " + "INNER JOIN FETCH h.type_hunter_id th " + "INNER JOIN FETCH h.type_nen_id tn " + "INNER JOIN FETCH h.type_blood_id ts " + "WHERE h.deleted_at IS NULL " + "ORDER BY h.id ASC", HunterEntity.class); long totalCount = entityManager.createQuery("SELECT COUNT(DISTINCT h.id) FROM HunterEntity h WHERE h.deleted_at IS NULL", Long.class).getSingleResult(); query.setFirstResult(page * size); query.setMaxResults(size); List<HunterEntity> hunters = query.getResultList(); // 重写总页数计算逻辑 return new PageImpl<>(hunters, PageRequest.of(page, size), totalCount) { @Override public int getTotalPages() { return getTotalElements() == 0 ? 1 : super.getTotalPages(); } }; }
2. 分页数据混乱问题
核心原因是前后端页码索引不一致:
- 后端Spring Data的
PageRequest采用0-based索引(page从0开始计数) - 前端模板生成的是1-based页码(从1开始),但跳转时直接将1-based页码传给后端,导致
setFirstResult(page * size)计算错误:- 前端点击第1页,传page=1,后端起始位置为
1*size,跳过第一页数据,实际返回第二页内容 - 前端点击第2页,传page=2,起始位置为
2*size,若总数据不足则返回空列表
- 前端点击第1页,传page=1,后端起始位置为
修复方法:
(1)调整前端跳转逻辑
将前端页码转换为0-based索引后再传递:
<div class="pagination justify-content-center"> <ul class="pagination"> <li class="page-item" th:each="i: ${#numbers.sequence(1, totalPages)}" th:class="${currentPage == i} ? 'active' : ''"> <a class="page-link" th:href="@{'/hunters/list?page=' + ${i - 1}}">[[${i}]]</a> </li> </ul> </div>
(2)同步前端当前页显示
Controller中传递给前端的currentPage转换为1-based:
@GetMapping("/list") public String listHunters(Model model, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) { // 页码合法性校验,避免负数 if (page < 0) { page = 0; } Page<HunterEntity> hunterPage = hunterService.indexPagination(page, size); // 若页码超出总页数,跳转到最后一页 if (hunterPage.getTotalPages() > 0 && page >= hunterPage.getTotalPages()) { page = hunterPage.getTotalPages() - 1; hunterPage = hunterService.indexPagination(page, size); } model.addAttribute("hunters", hunterPage.getContent()); model.addAttribute("currentPage", page + 1); // 转换为1-based给前端 model.addAttribute("totalPages", hunterPage.getTotalPages()); return "/hunter/list-hunters"; }
(3)修正计数查询的潜在问题
列表查询使用JOIN FETCH可能返回重复主实体,导致COUNT(h)统计不准确,改用COUNT(DISTINCT h.id)保证计数正确:
long totalCount = entityManager.createQuery( "SELECT COUNT(DISTINCT h.id) FROM HunterEntity h WHERE h.deleted_at IS NULL", Long.class ).getSingleResult();
内容的提问来源于stack exchange,提问作者user23450337
相关产品推荐
相关产品推荐

