You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)调整前端跳转逻辑

将前端页码转换为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:58:13