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

MyBatisPlus 与 Thymeleaf 全栈分页实战

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

橘子海发布于 2026/3/29更新于 2026/7/2437 浏览
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. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 亚洲艺术电影节携澳门文化亮相深圳
  • CentOS 7 Docker 安装指南
  • Android 核心源码与 Framework 精编解析指南
  • 解决 PKIX path building failed:SSL 证书导入 Java 信任库
  • SpringBoot 整合 LangChain4j 与 Tavily 实现联网搜索及 API Key 获取指南
  • 基于 Hunyuan-MT-7B-WEBUI 快速搭建多语言翻译系统
  • Servlet 原生 Response 实现文件下载
  • C++ 手写 List 容器实战:双向链表原理与源码实现
  • DeepSeek-R1-Distill-Llama-8B 优化技巧:提升文本生成质量
  • 基于开源鸿蒙(OpenHarmony)的【智能家居综合应用】系统
  • ToDesk ToClaw AI 科技新闻自动化推送实战
  • C++ 手写线程池:基于策略模式实现日志模块
  • 低代码诞生的背景:企业软件复杂度的累积
  • 前端技术演进趋势与百道场景化面试题精选
  • 队列的数组模拟实现与 STL queue 容器实战
  • Java transient 关键字详解与 Flink State 实践
  • 使用文心一言为智能体设计稳定调用工作流的提示词
  • 基于 ChatTTS 搭建支持情感控制的 TTS 服务
  • PyCharm 中 Copilot 插件 Claude 模型缺失问题排查
  • 后仿 SDF 反标 Warning 描述与解决方案

相关免费在线工具

  • 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