前言
在现代 Web 开发中,分页是平衡用户体验与系统性能的关键。无论是企业级后台还是面向用户的平台,合理的分页策略能有效降低服务器负载并提升交互流畅度。本文将通过 MyBatisPlus 和 Thymeleaf 的深度整合,展示如何构建一套完整的全栈分页方案,实现后端数据查询与前端页面渲染的无缝对接。
一、环境搭建及表结构
MyBatisPlus 作为 MyBatis 的增强版 ORM 框架,内置了强大的分页插件,能大幅简化分页逻辑的开发。我们基于 Spring Boot 项目,引入必要的依赖即可快速启动。
1. 依赖配置
在 pom.xml 中集成 MyBatisPlus、Lombok 以及 PostgreSQL 驱动:
<!-- 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;
// ... 其他字段省略 ...
}
2. 业务层分页逻辑
利用 MyBatisPlus 的 IPage 对象处理分页参数,结合 QueryWrapper 构建动态查询条件:
@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 对象时传入当前页码和每页条数,直接传递给 Mapper 执行查询,无需手写 SQL 分页语句。
3. 控制层接口
Controller 接收前端请求参数,调用 Service 层方法并返回统一结果封装:
@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 分页集成
虽然标题提及 Thymeleaf,但在实际 AJAX 异步加载场景中,通常由前端 JS 负责表格渲染与分页条交互。以下展示如何通过原生 JavaScript 或 jQuery 实现动态更新。
1. 表格数据渲染
定义基础 HTML 结构,通过 Ajax 获取后端 JSON 数据并填充表格:
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. 分页条交互
分页条需要支持上一页、下一页切换,并根据当前页状态禁用不可用按钮:
<div>
<nav aria-label="Table Pagination">
<ul id="pagination-list"></ul>
</nav>
</div>
function renderPagination(totalPages) {
const paginationContainer = document.getElementById('pagination-list');
paginationContainer.innerHTML = '';
if (totalPages <= 1) return;
// 上一页
paginationContainer.innerHTML += `
<li ${currentPage > 1 ? '' : 'disabled'}>
<a href="#" onclick="changePage(${totalPages},${currentPage - 1})" aria-label="Previous">前一页</a>
</li>
`;
// 下一页
paginationContainer.innerHTML += `
<li ${currentPage < totalPages ? '' : 'disabled'}>
<a href="#" onclick="changePage(${totalPages},${currentPage + 1})" aria-label="Next">后一页</a>
</li>
`;
}
注意修复了原代码中的标签闭合问题,确保 HTML 结构合法。
四、常见问题排查
1. 分页不生效
如果前端能看到数据但下方没有分页条,或者点击翻页无反应,通常是后端分页插件未正确配置。
2. 解决方案
检查是否注入了 MybatisPlusInterceptor 并添加了 PaginationInnerInterceptor:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor i = new MybatisPlusInterceptor();
i.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
return i;
}
}
配置完成后重启服务,分页功能即可正常运作。
五、总结
本文从环境搭建到前后端联调,完整演示了基于 MyBatisPlus 和 Thymeleaf(配合前端 JS)的全栈分页实现路径。重点解决了分页对象传递、拦截器配置及前端动态渲染等关键环节。掌握这一模式,可快速应用到各类列表查询场景中,提升开发效率与系统稳定性。


