跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

MyBatisPlus 与 Thymeleaf 全栈分页实战

基于 MyBatisPlus 和 Thymeleaf 构建全栈分页功能,涵盖环境搭建、实体类定义、Service 层分页逻辑、Controller 接口设计及前端 Ajax 交互与分页条渲染。重点解决分页插件配置缺失导致的分页失效问题,提供从后端查询到前端展示的完整代码示例及常见故障排查方案。

橘子海发布于 2026/3/29更新于 2026/9/1059 浏览
MyBatisPlus 与 Thymeleaf 全栈分页实战

MyBatisPlus 与 Thymeleaf 全栈分页实战

在现代 Web 开发中,分页是平衡用户体验与系统性能的关键。通过 MyBatisPlus 处理后端数据查询,结合 Thymeleaf 进行前端渲染,可以实现高效的全栈分页方案。本文将直接切入实战,从环境搭建到前后端交互,完整展示这一流程。

技术选型与目标

MyBatisPlus 作为 MyBatis 的增强工具,内置了强大的分页插件,能大幅减少手动编写 SQL 的工作量。Thymeleaf 则提供了优雅的模板引擎能力,支持静态预览和动态绑定。我们的目标是构建一个完整的分页模块,涵盖实体定义、Service 层逻辑、Controller 接口以及前端的 Ajax 请求与页面渲染。

环境搭建与表结构

1. Maven 依赖配置

在 pom.xml 中引入必要的依赖。这里以 PostgreSQL 数据库为例,同时集成 Lombok 简化代码。

<!-- mybatis-plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>
<!-- lombok 代码自动生成组件 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>
<!-- PostgreSql 驱动包 -->
<dependency>
    <groupId>net.postgis</groupId>
    <artifactId>postgis-jdbc</artifactId>
    <version>2.5.0</version>
</dependency>

2. 示例表结构

我们以城市停水信息数据为例,主要字段包括停水区域、时间、原因等。表结构如下:

表结构

对应的实例数据如下:

示例数据

Java 后台分页实现

1. 实体类定义

使用 Lombok 注解简化 Getter/Setter,配合 MyBatisPlus 注解映射数据库字段。

package org.yelang.pcwater.domain;

import java.io.Serializable;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@TableName(value = "biz_stop_water_info")
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class StopWaterInfo implements Serializable {
    private static final long serialVersionUID = -1582687729349525826L;

    @TableId(value = "pk_id")
    private Long pkId;

    @TableField(value = "affect_user")
    private String affectUser; // 停水客户

    @TableField(value = "affected_range")
    private String affectedRange; // 停水范围

    @TableField(value = "affect_region")
    private String affectRegion; // 停水区域

    @TableField(value = "created_on")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createdOn; // 原创建时间

    @TableField(value = "inform_emergency_plan")
    private String informEmergencyPlan; // 应急计划

    @TableField(value = "inform_id")
    private Integer informId; // 信息标识

    @TableField(value = "inform_remark")
    private String informRemark; // 备注信息

    @TableField(value = "main_lead_path")
    private String mainLeadPath; // 停水主管径

    private String position; // 停水地点
    private String reason; // 停水原因

    @TableField(value = "service_phone")
    private String servicePhone; // 服务热线

    @TableField(value = "show_end_date")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @SerializedName("showenddate")
    private Date showEndDate; // 显示结束时间

    @TableField(value = "stop_end_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date stopEndTime; // 停水结束时间

    @TableField(value = "stop_start_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date stopStartTime; // 停水开始时间

    @TableField(value = "stop_time")
    @SerializedName("stoptime")
    private String stopTime; // 停水时间

    @TableField(value = "stopwater_type")
    private String stopwaterType; // 停水类型

    @TableField(value = "supply_emergency_plan")
    private String supplyEmergencyPlan; // 应急供应计划

    @TableField(value = "supply_remark")
    private String supplyRemark; // 供应备注

    private String title; // 停水标题
    private String type; // 类型

    @TableField(value = "regin_name")
    private String reginName; // 区域名称

    @TableField(value = "sync_sno")
    private Long syncSno; // 同步批次号
}

2. 业务层分页逻辑

核心在于使用 IPage 对象封装分页信息。我们在 Service 接口中定义分页方法,并在实现类中利用 QueryWrapper 构建查询条件。

/**
 * 根据区域查询分页停水信息列表
 */
IPage<StopWaterInfo> page(Integer pageNum, Integer pageSize, String regionName);

具体实现如下:

@Override
public IPage<StopWaterInfo> page(Integer pageNum, Integer pageSize, String regionName) {
    QueryWrapper<StopWaterInfo> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("regin_name", regionName);
    queryWrapper.orderByDesc("created_on");
    
    Page<StopWaterInfo> page = new Page<>(pageNum, pageSize);
    return this.baseMapper.selectPage(page, queryWrapper);
}

这里构造 Page 对象传入当前页码和每页条数,配合 QueryWrapper 传递给 Mapper 执行查询。

3. 控制层接口

Controller 负责接收前端参数并返回结果。注意设置默认值以避免空指针异常。

@PostMapping("/list")
@ResponseBody
public AjaxResult list(
        @RequestParam(defaultValue = "1") Integer pageNum,
        @RequestParam(defaultValue = "5") Integer pageSize,
        @RequestParam(defaultValue = "") String regionName) {
    IPage<StopWaterInfo> page = waterInfoService.page(pageNum, pageSize, regionName);
    AjaxResult result = AjaxResult.success();
    result.put("data", page);
    return result;
}

Thymeleaf 分页集成

1. 表格数据渲染

前端通过 Ajax 请求后端接口,获取 JSON 数据后动态填充表格。以下是 HTML 结构和 JS 逻辑。

HTML 结构:

<div>
    <table>
        <thead>
            <tr>
                <th>创建时间</th>
                <th>停水地点</th>
                <th>原因</th>
            </tr>
        </thead>
        <tbody id="table-body">
            <!-- 数据行将通过 JS 插入 -->
        </tbody>
    </table>
</div>

JS 加载逻辑:

function renderTablePage(page) {
    currentPage = page;
    const tableBody = document.getElementById('table-body');
    tableBody.innerHTML = '';
    
    $.ajax({
        type: "post",
        url: ctx + "datasync/list",
        dataType: "json",
        cache: false,
        processData: true,
        data: { "pageNum": page, "pageSize": EVENTS_PER_PAGE, "regionName": "" },
        success: function(result) {
            const pageData = result.data.records;
            const totalPages = result.data.pages;
            
            if (pageData.length === 0) {
                tableBody.innerHTML = `<tr><td colspan="5">未找到符合条件的停水事件。</td></tr>`;
            } else {
                pageData.forEach(event => {
                    const row = tableBody.insertRow();
                    row.innerHTML = `
                        <td>${event.createdOn}</td>
                        <td>${event.position}</td>
                        <td>${event.reason}</td>
                    `;
                });
            }
            renderPagination(totalPages);
        },
        error: function() {
            tableBody.innerHTML = `<tr><td colspan="5">未找到符合条件的停水事件。</td></tr>`;
        }
    });
}

2. 分页条组件

分页条需要独立容器,并根据总页数动态生成上一页/下一页按钮。

HTML 容器:

<div>
    <nav aria-label="Table Pagination">
        <ul id="table-pagination"></ul>
    </nav>
</div>

JS 渲染逻辑(修复了原始片段中的语法错误):

function renderPagination(totalPages) {
    const paginationContainer = document.getElementById('table-pagination');
    paginationContainer.innerHTML = '';
    
    if (totalPages <= 1) return;
    
    // 上一页
    const prevDisabled = currentPage <= 1 ? 'disabled' : '';
    paginationContainer.innerHTML += `
        <li ${prevDisabled}>
            <a href="#" onclick="changePage(${totalPages},${currentPage - 1})" aria-label="Previous">前一页</a>
        </li>
    `;
    
    // 下一页
    const nextDisabled = currentPage >= totalPages ? 'disabled' : '';
    paginationContainer.innerHTML += `
        <li ${nextDisabled}>
            <a href="#" onclick="changePage(${totalPages},${currentPage + 1})" aria-label="Next">后一页</a>
        </li>
    `;
}

最终效果如下:

成果展示

常见问题排查

分页不生效

如果页面展示了数据但下方没有分页条,或者所有数据一次性加载,通常是分页插件未正确配置导致的。

问题截图

解决方案

需要在 Spring Boot 配置类中注册 MybatisPlusInterceptor 并添加 PaginationInnerInterceptor。

package org.yelang.pcwater.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor i = new MybatisPlusInterceptor();
        i.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
        return i;
    }
}

配置完成后重启服务,分页功能即可正常运作。

总结

本文详细演示了基于 MyBatisPlus 和 Thymeleaf 的全栈分页实现路径。从 Maven 依赖引入、实体映射、Service 层分页查询,到 Controller 接口暴露及前端 Ajax 交互,每一步都提供了可运行的代码参考。重点解决了分页插件配置缺失这一常见坑点。掌握这套方案,能有效提升开发效率并确保系统的稳定性。

目录

  1. MyBatisPlus 与 Thymeleaf 全栈分页实战
  2. 技术选型与目标
  3. 环境搭建与表结构
  4. 1. Maven 依赖配置
  5. 2. 示例表结构
  6. Java 后台分页实现
  7. 1. 实体类定义
  8. 2. 业务层分页逻辑
  9. 3. 控制层接口
  10. Thymeleaf 分页集成
  11. 1. 表格数据渲染
  12. 2. 分页条组件
  13. 常见问题排查
  14. 分页不生效
  15. 解决方案
  16. 总结

更多推荐文章

查看全部
  • OmniSteward:基于 LLM Agent 的语音文字智能家居与电脑控制系统
  • Python3 基本类型运算详解
  • GESP C++二级编程题:小杨的 H 字矩阵
  • API 基本概念、功能分类、认证方式、使用方法和开发流程
  • 开源 ChatClaw:支持 AI 问答、任务自动化及本地知识库
  • GitHub Copilot 四种模式区别:Agent、Ask、Edit、Plan
  • AI Agent 新范式:基于 FastGPT 与 MCP 协议构建工具增强型智能体
  • VSCode Copilot 登录失败排查与修复指南
  • Windows下MySQL 8.4压缩包安装手记
  • Windows 下 Python 新包管理工具 uv 安装与 VSCode 配置
  • C++ 标准库 string 类全面指南
  • VS Code 运行前端代码
  • 微博爬虫 Web 管理界面配置手册
  • Windows 下安装和配置 Git 的实用笔记
  • 3个步骤让你的Home Assistant界面焕然一新:智能家居UI设计全攻略
  • GitHub Copilot Plan 模式核心优势与适用场景解析
  • Cursor AI 编辑器安装配置与快捷键详解
  • 基于 YOLO 标注格式的无人机航拍人员搜救检测数据集
  • C语言指针与数组的深度关联及实战应用
  • 蓝桥杯 C/C++ 组基础语法入门:C++ 知识要点(上)

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online