MyBatis分頁(yè)查詢(xún)實(shí)戰(zhàn)案例完整流程
簡(jiǎn)介:MyBatis是一個(gè)強(qiáng)大的Java持久層框架,支持自定義SQL和高級(jí)映射。在處理大數(shù)據(jù)量場(chǎng)景時(shí),分頁(yè)查詢(xún)是提升系統(tǒng)性能的重要手段。本案例以員工工資信息管理為例,詳細(xì)講解如何在IDEA中使用MyBatis結(jié)合PageHelper插件實(shí)現(xiàn)分頁(yè)查詢(xún)功能。內(nèi)容涵蓋PageHelper的引入方式、Mapper接口的定義、服務(wù)層與控制層的調(diào)用流程,以及前后端交互的分頁(yè)數(shù)據(jù)結(jié)構(gòu)。通過(guò)該實(shí)戰(zhàn)案例,開(kāi)發(fā)者可以快速掌握MyBatis分頁(yè)查詢(xún)的完整實(shí)現(xiàn)流程,提升開(kāi)發(fā)效率與系統(tǒng)性能。

1. MyBatis框架簡(jiǎn)介
MyBatis 是一個(gè)輕量級(jí)但功能強(qiáng)大的持久層框架,它摒棄了傳統(tǒng)的全自動(dòng)ORM映射方式,轉(zhuǎn)而提供靈活的 SQL 控制能力。通過(guò) XML 配置文件或注解方式,開(kāi)發(fā)者可以精準(zhǔn)定義 SQL 語(yǔ)句,并實(shí)現(xiàn) Java 對(duì)象與數(shù)據(jù)庫(kù)記錄之間的映射。其核心組件包括 SqlSessionFactory 、 SqlSession 、 Mapper 接口及 XML 映射文件,構(gòu)成了一個(gè)松耦合、易擴(kuò)展的數(shù)據(jù)訪問(wèn)層架構(gòu)。在企業(yè)級(jí)開(kāi)發(fā)中,MyBatis 憑借其高性能、易調(diào)試和靈活的 SQL 管理機(jī)制,被廣泛應(yīng)用于復(fù)雜業(yè)務(wù)場(chǎng)景下的數(shù)據(jù)持久化處理,為實(shí)現(xiàn)如分頁(yè)查詢(xún)等功能提供了堅(jiān)實(shí)的基礎(chǔ)。
2. 分頁(yè)查詢(xún)?cè)砼c應(yīng)用場(chǎng)景
2.1 分頁(yè)查詢(xún)的基本原理
2.1.1 分頁(yè)查詢(xún)的定義
分頁(yè)查詢(xún)(Pagination Query)是指在處理大量數(shù)據(jù)時(shí),將數(shù)據(jù)按照一定數(shù)量劃分為多個(gè)“頁(yè)”進(jìn)行查詢(xún)和展示的技術(shù)。在Web應(yīng)用中,這種機(jī)制被廣泛使用,尤其是在處理成千上萬(wàn)條數(shù)據(jù)時(shí),避免一次性加載全部數(shù)據(jù)到前端,從而提升用戶(hù)體驗(yàn)和系統(tǒng)性能。
分頁(yè)的核心思想是: 每次只加載用戶(hù)當(dāng)前需要查看的數(shù)據(jù),而不是全部數(shù)據(jù) 。通過(guò)這種方式,可以有效降低服務(wù)器壓力,提升響應(yīng)速度,并優(yōu)化數(shù)據(jù)庫(kù)查詢(xún)效率。
2.1.2 分頁(yè)機(jī)制的工作流程
分頁(yè)機(jī)制的典型工作流程如下:
- 前端請(qǐng)求 :用戶(hù)在頁(yè)面上點(diǎn)擊下一頁(yè)、上一頁(yè)或跳轉(zhuǎn)頁(yè)碼。
- 參數(shù)傳遞 :前端將當(dāng)前頁(yè)碼(pageNum)和每頁(yè)顯示數(shù)量(pageSize)作為參數(shù)傳遞給后端接口。
- 后端處理 :
- 解析請(qǐng)求參數(shù) pageNum 和 pageSize;
- 構(gòu)建帶分頁(yè)條件的 SQL 查詢(xún)語(yǔ)句;
- 執(zhí)行查詢(xún),獲取當(dāng)前頁(yè)數(shù)據(jù);
- 同時(shí)獲取總記錄數(shù)用于計(jì)算總頁(yè)數(shù); - 結(jié)果返回 :將當(dāng)前頁(yè)數(shù)據(jù)和分頁(yè)信息(如總記錄數(shù)、總頁(yè)數(shù))封裝后返回給前端;
- 前端展示 :前端根據(jù)返回?cái)?shù)據(jù)展示當(dāng)前頁(yè)數(shù)據(jù),并渲染分頁(yè)控件。
2.1.3 數(shù)據(jù)庫(kù)層面的分頁(yè)實(shí)現(xiàn)方式
不同的數(shù)據(jù)庫(kù)系統(tǒng)支持的分頁(yè)語(yǔ)法略有不同。以下是主流數(shù)據(jù)庫(kù)的分頁(yè)實(shí)現(xiàn)方式:
| 數(shù)據(jù)庫(kù)類(lèi)型 | 分頁(yè)語(yǔ)法示例 | 說(shuō)明 |
|---|---|---|
| MySQL | SELECT * FROM users LIMIT 10 OFFSET 20 | LIMIT 指定每頁(yè)條數(shù), OFFSET 指定偏移量 |
| PostgreSQL | SELECT * FROM users LIMIT 10 OFFSET 20 | 與MySQL語(yǔ)法一致 |
| Oracle | SELECT * FROM (SELECT A.*, ROWNUM RN FROM (SELECT * FROM users) A WHERE ROWNUM <= 20) WHERE RN > 10 | 使用嵌套子查詢(xún)和ROWNUM實(shí)現(xiàn)分頁(yè) |
| SQL Server | SELECT * FROM users ORDER BY id OFFSET 10 ROWS FETCH NEXT 20 ROWS ONLY | 使用OFFSET和FETCH實(shí)現(xiàn)分頁(yè) |
以MySQL為例的分頁(yè)SQL解析:
SELECT * FROM users LIMIT 10 OFFSET 20;
LIMIT 10:每頁(yè)顯示10條數(shù)據(jù);OFFSET 20:從第21條數(shù)據(jù)開(kāi)始(即跳過(guò)前20條);
該SQL語(yǔ)句將返回第3頁(yè)的數(shù)據(jù)(假設(shè)每頁(yè)10條),即第21~30條記錄。
2.2 分頁(yè)查詢(xún)的典型應(yīng)用場(chǎng)景
2.2.1 Web應(yīng)用中的數(shù)據(jù)列表展示
在Web應(yīng)用中,數(shù)據(jù)列表是常見(jiàn)的展示形式,例如用戶(hù)管理、訂單列表、商品信息等。由于數(shù)據(jù)量可能非常龐大,一次性加載所有數(shù)據(jù)不僅影響用戶(hù)體驗(yàn),還會(huì)造成資源浪費(fèi)。
分頁(yè)在數(shù)據(jù)列表中的作用:
- 減少頁(yè)面加載時(shí)間;
- 提高頁(yè)面響應(yīng)速度;
- 提升用戶(hù)體驗(yàn),避免信息過(guò)載;
- 便于數(shù)據(jù)的瀏覽與導(dǎo)航。
示例:
一個(gè)電商系統(tǒng)中,商品總數(shù)為10萬(wàn)條。若一次性加載所有商品信息,頁(yè)面加載時(shí)間將極長(zhǎng),甚至導(dǎo)致瀏覽器卡頓。使用分頁(yè)機(jī)制后,可以每次只加載100條數(shù)據(jù),用戶(hù)點(diǎn)擊翻頁(yè)時(shí)再加載后續(xù)數(shù)據(jù)。
2.2.2 大數(shù)據(jù)量下的性能優(yōu)化需求
在處理大數(shù)據(jù)量時(shí),數(shù)據(jù)庫(kù)查詢(xún)的性能尤為重要。如果不對(duì)查詢(xún)進(jìn)行分頁(yè)限制,可能會(huì)導(dǎo)致以下問(wèn)題:
- 查詢(xún)響應(yīng)時(shí)間長(zhǎng) :全表掃描會(huì)消耗大量資源;
- 內(nèi)存占用高 :一次性加載大量數(shù)據(jù)可能超出內(nèi)存限制;
- 數(shù)據(jù)庫(kù)連接阻塞 :長(zhǎng)時(shí)間的查詢(xún)會(huì)占用數(shù)據(jù)庫(kù)連接,影響其他操作。
解決方案:
使用分頁(yè)查詢(xún)可以有效限制返回?cái)?shù)據(jù)量,從而:
- 減少數(shù)據(jù)庫(kù)掃描的數(shù)據(jù)量;
- 降低I/O和CPU使用率;
- 縮短查詢(xún)響應(yīng)時(shí)間;
- 提高并發(fā)處理能力。
性能優(yōu)化技巧:
- 使用索引字段作為排序條件;
- 避免使用
SELECT *,只查詢(xún)需要的字段; - 對(duì)查詢(xún)條件進(jìn)行合理索引;
- 分頁(yè)深度優(yōu)化(如游標(biāo)分頁(yè))。
2.2.3 分頁(yè)在前后端交互中的作用
分頁(yè)查詢(xún)不僅是一種數(shù)據(jù)庫(kù)層面的技術(shù),它在前后端交互中也扮演著重要角色。
前端視角:
- 分頁(yè)控件(如“上一頁(yè)”、“下一頁(yè)”、“跳轉(zhuǎn)頁(yè)碼”)提供良好的用戶(hù)交互體驗(yàn);
- 支持動(dòng)態(tài)加載數(shù)據(jù),實(shí)現(xiàn)懶加載或無(wú)限滾動(dòng);
- 提供分頁(yè)狀態(tài)管理(如當(dāng)前頁(yè)、總頁(yè)數(shù)、總記錄數(shù));
后端視角:
- 分頁(yè)接口設(shè)計(jì)規(guī)范統(tǒng)一;
- 接口返回結(jié)構(gòu)標(biāo)準(zhǔn)化(如包含數(shù)據(jù)列表、總記錄數(shù)、當(dāng)前頁(yè)碼等);
- 分頁(yè)信息封裝為對(duì)象(如PageInfo)以便前端處理;
前后端交互流程示意圖(mermaid):
sequenceDiagram
用戶(hù)->>前端: 點(diǎn)擊下一頁(yè)
前端->>后端: 發(fā)送pageNum=2, pageSize=10
后端->>數(shù)據(jù)庫(kù): 執(zhí)行分頁(yè)SQL查詢(xún)
數(shù)據(jù)庫(kù)-->>后端: 返回第2頁(yè)數(shù)據(jù)
后端-->>前端: 返回JSON格式分頁(yè)結(jié)果
前端-->>用戶(hù): 展示第2頁(yè)數(shù)據(jù)2.3 MyBatis中實(shí)現(xiàn)分頁(yè)的方式概述
2.3.1 手動(dòng)拼接SQL分頁(yè)
手動(dòng)拼接SQL是最原始的分頁(yè)實(shí)現(xiàn)方式,開(kāi)發(fā)者需要在SQL語(yǔ)句中根據(jù)頁(yè)碼和每頁(yè)大小動(dòng)態(tài)計(jì)算偏移量并拼接LIMIT/OFFSET。
示例代碼(MyBatis XML):
<select id="selectUsers" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
LIMIT #{pageSize} OFFSET #{offset}
</select>Java調(diào)用示例:
int pageNum = 2; int pageSize = 10; int offset = (pageNum - 1) * pageSize; List<User> users = userMapper.selectUsers(offset, pageSize);
優(yōu)點(diǎn):
- 靈活,適用于各種數(shù)據(jù)庫(kù);
- 無(wú)需依賴(lài)第三方插件;
缺點(diǎn):
- 需要手動(dòng)計(jì)算偏移量;
- 不利于代碼復(fù)用;
- 分頁(yè)邏輯分散在業(yè)務(wù)代碼中;
2.3.2 使用PageHelper插件自動(dòng)分頁(yè)
PageHelper是MyBatis官方推薦的分頁(yè)插件,它可以自動(dòng)攔截SQL并添加分頁(yè)語(yǔ)句,大大簡(jiǎn)化了分頁(yè)邏輯的實(shí)現(xiàn)。
使用示例:
PageHelper.startPage(pageNum, pageSize); List<User> users = userMapper.selectAll(); PageInfo<User> pageInfo = new PageInfo<>(users);
對(duì)應(yīng)的SQL執(zhí)行過(guò)程:
SELECT * FROM users LIMIT 10 OFFSET 10; SELECT COUNT(*) FROM users;
優(yōu)點(diǎn):
- 簡(jiǎn)潔高效,一行代碼實(shí)現(xiàn)分頁(yè);
- 自動(dòng)處理COUNT查詢(xún);
- 支持多種數(shù)據(jù)庫(kù);
- 與MyBatis無(wú)縫集成;
缺點(diǎn):
- 依賴(lài)PageHelper插件;
- 對(duì)某些復(fù)雜查詢(xún)支持有限;
2.3.3 分頁(yè)插件與原生SQL的對(duì)比分析
| 特性 | 原生SQL手動(dòng)分頁(yè) | PageHelper插件分頁(yè) |
|---|---|---|
| 實(shí)現(xiàn)方式 | 手動(dòng)拼接LIMIT/OFFSET | 自動(dòng)攔截并修改SQL |
| 分頁(yè)邏輯 | 分散在業(yè)務(wù)代碼中 | 集中統(tǒng)一處理 |
| 維護(hù)成本 | 高,容易出錯(cuò) | 低,易于維護(hù) |
| 支持COUNT查詢(xún) | 需手動(dòng)編寫(xiě) | 自動(dòng)添加 |
| 靈活性 | 高,可自定義 | 依賴(lài)插件配置 |
| 性能影響 | 無(wú)額外開(kāi)銷(xiāo) | 插件有一定性能開(kāi)銷(xiāo) |
| 推薦場(chǎng)景 | 簡(jiǎn)單查詢(xún)、數(shù)據(jù)庫(kù)兼容要求高 | 中大型項(xiàng)目、快速開(kāi)發(fā) |
結(jié)論:
- 對(duì)于小型項(xiàng)目或特定數(shù)據(jù)庫(kù)兼容需求,可以使用原生SQL手動(dòng)分頁(yè);
- 對(duì)于中大型項(xiàng)目、快速開(kāi)發(fā)和標(biāo)準(zhǔn)化分頁(yè)處理,推薦使用PageHelper插件;
- 在使用PageHelper時(shí),需注意其對(duì)復(fù)雜查詢(xún)(如JOIN、子查詢(xún))的兼容性,并結(jié)合實(shí)際情況選擇使用方式。
3. PageHelper插件集成與配置
在實(shí)際開(kāi)發(fā)中,處理大量數(shù)據(jù)時(shí)分頁(yè)查詢(xún)是必不可少的。PageHelper 是 MyBatis 中非常流行的一款分頁(yè)插件,它能夠簡(jiǎn)化分頁(yè)邏輯,避免手動(dòng)拼接 SQL 分頁(yè)語(yǔ)句的復(fù)雜性。本章將深入講解如何將 PageHelper 插件集成到項(xiàng)目中,并進(jìn)行相關(guān)配置與使用,為后續(xù)章節(jié)中分頁(yè)方法的實(shí)現(xiàn)打下堅(jiān)實(shí)基礎(chǔ)。
3.1 PageHelper插件概述
PageHelper 是一個(gè)專(zhuān)為 MyBatis 設(shè)計(jì)的分頁(yè)插件,能夠自動(dòng)識(shí)別 SQL 語(yǔ)句并進(jìn)行分頁(yè)處理,極大提升了開(kāi)發(fā)效率和代碼可維護(hù)性。
3.1.1 PageHelper的功能與優(yōu)勢(shì)
PageHelper 提供了以下核心功能:
- 自動(dòng)分頁(yè) :通過(guò)簡(jiǎn)單的 API 調(diào)用即可實(shí)現(xiàn) SQL 分頁(yè)。
- 多數(shù)據(jù)庫(kù)支持 :支持 MySQL、Oracle、SQL Server、PostgreSQL 等主流數(shù)據(jù)庫(kù)。
- 靈活配置 :允許配置分頁(yè)參數(shù)、合理化分頁(yè)、默認(rèn)頁(yè)面大小等。
- 兼容性好 :與 Spring、Spring Boot、MyBatis 注解和 XML 映射方式兼容良好。
- 性能優(yōu)化 :底層采用攔截器機(jī)制,對(duì) SQL 進(jìn)行改寫(xiě),減少冗余操作。
相較于手動(dòng)拼接 SQL 分頁(yè),使用 PageHelper 的優(yōu)勢(shì)在于:
- 開(kāi)發(fā)效率高 :減少重復(fù)代碼。
- 可維護(hù)性強(qiáng) :集中管理分頁(yè)邏輯。
- 擴(kuò)展性好 :支持多種數(shù)據(jù)庫(kù)和自定義分頁(yè)策略。
3.1.2 插件的版本選擇與兼容性分析
PageHelper 的版本更新頻繁,不同版本與 MyBatis 和 Spring Boot 的兼容性略有不同。常見(jiàn)版本如下:
| PageHelper 版本 | MyBatis 版本要求 | Spring Boot 版本建議 | 說(shuō)明 |
|---|---|---|---|
| 5.2.0 | ≥3.4.0 | ≥2.0.0 | 最新穩(wěn)定版,推薦使用 |
| 5.1.11 | ≥3.4.0 | ≥1.5.0 | 穩(wěn)定版本,廣泛使用 |
| 4.x | 3.x | 1.x | 舊版,不推薦新項(xiàng)目使用 |
在選擇版本時(shí),應(yīng)優(yōu)先考慮與當(dāng)前項(xiàng)目依賴(lài)版本的匹配性。推薦使用 5.2.0 或 5.1.11 版本。
3.2 PageHelper的引入與配置
為了在項(xiàng)目中使用 PageHelper,需要將其作為依賴(lài)引入,并在 MyBatis 或 Spring Boot 配置文件中進(jìn)行相應(yīng)設(shè)置。
3.2.1 Maven依賴(lài)的引入方式
在 pom.xml 中添加如下依賴(lài):
<!-- MyBatis PageHelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>5.2.0</version>
</dependency>說(shuō)明:
pagehelper-spring-boot-starter是 Spring Boot 項(xiàng)目專(zhuān)用的 starter 模塊,自動(dòng)完成插件的配置。- 若非 Spring Boot 項(xiàng)目,可使用
pagehelper模塊,并手動(dòng)配置插件。
3.2.2 在MyBatis配置文件中添加插件配置
對(duì)于非 Spring Boot 項(xiàng)目(傳統(tǒng) Spring + MyBatis),需在 mybatis-config.xml 中手動(dòng)添加插件配置:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 可選參數(shù) -->
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
<property name="supportMethodsArguments" value="true"/>
<property name="params" value="count=countSql"/>
</plugin>
</plugins>參數(shù)說(shuō)明:
| 參數(shù)名 | 含義 | 示例值 |
|---|---|---|
| helperDialect | 指定數(shù)據(jù)庫(kù)方言 | mysql、oracle、sqlserver |
| reasonable | 是否啟用合理化分頁(yè)(頁(yè)碼超出范圍時(shí)自動(dòng)調(diào)整) | true、false |
| supportMethodsArguments | 是否支持通過(guò) Mapper 接口參數(shù)傳遞分頁(yè)信息 | true |
| params | 配置 count 查詢(xún)的參數(shù)名 | count=countSql |
3.2.3 Spring Boot項(xiàng)目中的自動(dòng)配置方式
在 Spring Boot 項(xiàng)目中,除了引入依賴(lài),還可以通過(guò) application.yml 或 application.properties 文件進(jìn)行配置:
pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: count=countSql
說(shuō)明:
helper-dialect:指定數(shù)據(jù)庫(kù)類(lèi)型,用于分頁(yè) SQL 的自動(dòng)改寫(xiě)。reasonable:開(kāi)啟合理化分頁(yè),如當(dāng)前頁(yè)碼大于最大頁(yè)碼時(shí),自動(dòng)返回最后一頁(yè)數(shù)據(jù)。support-methods-arguments:?jiǎn)⒂媒涌诜椒▍?shù)中分頁(yè)信息的自動(dòng)識(shí)別。params:定義 count 查詢(xún)參數(shù)名稱(chēng)。
3.3 PageHelper的基本使用流程
PageHelper 的使用流程主要包括分頁(yè)前的準(zhǔn)備、分頁(yè)方法的調(diào)用以及分頁(yè)結(jié)果的獲取。
3.3.1 分頁(yè)前的準(zhǔn)備工作
在調(diào)用分頁(yè)方法之前,必須先調(diào)用 PageHelper.startPage(pageNum, pageSize) 方法,該方法會(huì)設(shè)置當(dāng)前線(xiàn)程的分頁(yè)上下文。
import com.github.pagehelper.PageHelper;
import java.util.List;
public List<User> getUsers(int pageNum, int pageSize) {
// 開(kāi)啟分頁(yè)
PageHelper.startPage(pageNum, pageSize);
// 查詢(xún)用戶(hù)列表(自動(dòng)分頁(yè))
return userMapper.selectAll();
}說(shuō)明:
pageNum:當(dāng)前頁(yè)碼,從 1 開(kāi)始。pageSize:每頁(yè)顯示的數(shù)據(jù)條數(shù)。- 該方法必須在查詢(xún)語(yǔ)句之前調(diào)用,否則分頁(yè)失效。
3.3.2 分頁(yè)方法調(diào)用與結(jié)果返回
調(diào)用 PageHelper.startPage() 后,緊接著執(zhí)行查詢(xún)語(yǔ)句即可。PageHelper 會(huì)自動(dòng)攔截 SQL,并添加 LIMIT 子句實(shí)現(xiàn)分頁(yè)。
執(zhí)行查詢(xún)后,可以通過(guò) PageInfo<T> 類(lèi)封裝分頁(yè)信息:
import com.github.pagehelper.PageInfo;
public PageInfo<User> getUsers(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAll();
return new PageInfo<>(users);
}PageInfo 包含了豐富的分頁(yè)信息,如總記錄數(shù)、當(dāng)前頁(yè)、總頁(yè)數(shù)等,便于前端展示。
3.3.3 常見(jiàn)配置參數(shù)的使用說(shuō)明
| 參數(shù) | 作用 | 使用示例 |
|---|---|---|
pageNum | 當(dāng)前頁(yè)碼 | PageHelper.startPage(2, 10); |
pageSize | 每頁(yè)數(shù)量 | PageHelper.startPage(1, 20); |
count | 是否執(zhí)行 count 查詢(xún) | 默認(rèn)為 true,可通過(guò) PageHelper.startPage(pageNum, pageSize, false) 關(guān)閉 |
reasonable | 是否啟用合理化分頁(yè) | 在配置文件中設(shè)置為 true |
pageSizeZero | 是否允許 pageSize 為 0 | 可設(shè)置為 true 表示不分頁(yè) |
示例:關(guān)閉 count 查詢(xún)
在某些場(chǎng)景下,如果已知總記錄數(shù),可避免重復(fù)執(zhí)行 count 查詢(xún):
PageHelper.startPage(1, 10, false); List<User> users = userMapper.selectAll();
此時(shí) PageHelper 不會(huì)生成 count SQL,從而提高性能。
示例:分頁(yè)結(jié)果封裝為 PageInfo
PageInfo<User> pageInfo = new PageInfo<>(users);
System.out.println("當(dāng)前頁(yè)碼:" + pageInfo.getPageNum());
System.out.println("每頁(yè)數(shù)量:" + pageInfo.getPageSize());
System.out.println("總記錄數(shù):" + pageInfo.getTotal());
System.out.println("總頁(yè)數(shù):" + pageInfo.getPages());輸出結(jié)果如下:
當(dāng)前頁(yè)碼:1
每頁(yè)數(shù)量:10
總記錄數(shù):123
總頁(yè)數(shù):13
流程圖:PageHelper分頁(yè)執(zhí)行流程
graph TD
A[調(diào)用PageHelper.startPage] --> B{判斷是否合理化分頁(yè)}
B -->|是| C[自動(dòng)調(diào)整頁(yè)碼]
B -->|否| D[使用原始頁(yè)碼]
C --> E[生成分頁(yè)SQL]
D --> E
E --> F[執(zhí)行SQL查詢(xún)]
F --> G[返回分頁(yè)數(shù)據(jù)]
G --> H[創(chuàng)建PageInfo對(duì)象]
H --> I[返回給調(diào)用方]該流程圖清晰地展示了 PageHelper 分頁(yè)的核心執(zhí)行流程,從開(kāi)始分頁(yè)到最終封裝返回?cái)?shù)據(jù)的全過(guò)程。
通過(guò)本章的學(xué)習(xí),我們了解了 PageHelper 的功能與優(yōu)勢(shì),掌握了其在 Maven 項(xiàng)目中的引入方式,以及在 MyBatis 和 Spring Boot 中的配置方法,并詳細(xì)講解了其基本使用流程及常見(jiàn)參數(shù)設(shè)置。下一章將圍繞 Mapper 接口設(shè)計(jì)分頁(yè)方法展開(kāi),進(jìn)一步深入 MyBatis 分頁(yè)的實(shí)踐應(yīng)用。
4. Mapper接口分頁(yè)方法設(shè)計(jì)
在 MyBatis 框架中,Mapper 接口是實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作的核心部分。設(shè)計(jì)良好的 Mapper 接口不僅能夠提高代碼的可維護(hù)性,還能有效支持分頁(yè)查詢(xún)功能。本章將深入探討 Mapper 接口中分頁(yè)方法的設(shè)計(jì)規(guī)范、分頁(yè) SQL 的編寫(xiě)與優(yōu)化策略,以及接口測(cè)試與驗(yàn)證方法,幫助開(kāi)發(fā)者構(gòu)建高效、穩(wěn)定的分頁(yè)機(jī)制。
4.1 Mapper接口的設(shè)計(jì)規(guī)范
4.1.1 接口命名與SQL映射關(guān)系
在設(shè)計(jì) Mapper 接口時(shí),良好的命名規(guī)范有助于提高代碼的可讀性和維護(hù)性。通常建議使用 IEntityMapper 的命名方式,其中 Entity 表示操作的實(shí)體對(duì)象,例如 IUserMapper 、 IOrderMapper 。
接口中的方法名應(yīng)與 XML 映射文件中的 SQL ID 保持一致,以確保 MyBatis 能正確綁定方法與 SQL 語(yǔ)句。例如:
public interface IUserMapper {
List<User> selectAllUsers();
}對(duì)應(yīng)的 XML 文件中:
<select id="selectAllUsers" resultType="User">
SELECT * FROM users
</select>這種命名方式不僅提高了代碼的可讀性,也便于后期維護(hù)和調(diào)試。
4.1.2 使用PageHelper進(jìn)行分頁(yè)的方法定義
在使用 PageHelper 插件進(jìn)行分頁(yè)時(shí),Mapper 接口中的方法設(shè)計(jì)需遵循一定的規(guī)范。PageHelper 通過(guò)攔截查詢(xún)語(yǔ)句,自動(dòng)為其添加分頁(yè)邏輯,因此接口方法只需返回 List<T> 類(lèi)型即可,PageHelper 會(huì)在執(zhí)行過(guò)程中自動(dòng)封裝分頁(yè)信息。
示例代碼如下:
public interface IUserMapper {
List<User> selectAllUsers();
}在調(diào)用該方法前,需先調(diào)用 PageHelper.startPage(pageNum, pageSize) 方法啟動(dòng)分頁(yè):
PageHelper.startPage(1, 10); List<User> users = userMapper.selectAllUsers();
PageHelper 會(huì)自動(dòng)對(duì) selectAllUsers() 方法的查詢(xún)結(jié)果進(jìn)行分頁(yè)處理,并封裝成 PageInfo 對(duì)象,便于后續(xù)使用。
4.2 分頁(yè)SQL語(yǔ)句的編寫(xiě)與優(yōu)化
4.2.1 動(dòng)態(tài)SQL與分頁(yè)結(jié)合使用
在實(shí)際開(kāi)發(fā)中,經(jīng)常需要根據(jù)不同的條件進(jìn)行分頁(yè)查詢(xún)。MyBatis 提供了 <if> 、 <choose> 、 <where> 等標(biāo)簽,用于構(gòu)建動(dòng)態(tài) SQL,從而實(shí)現(xiàn)靈活的查詢(xún)邏輯。
例如,以下是一個(gè)帶有動(dòng)態(tài)查詢(xún)條件的分頁(yè) SQL 示例:
<select id="selectUsersByCondition" resultType="User">
SELECT * FROM users
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select>在 Java 代碼中調(diào)用:
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectUsersByCondition("john", null);該查詢(xún)會(huì)根據(jù)傳入的參數(shù)動(dòng)態(tài)生成 SQL,確保分頁(yè)結(jié)果的準(zhǔn)確性。
4.2.2 分頁(yè)SQL的性能考量與優(yōu)化技巧
分頁(yè)查詢(xún)?cè)谔幚泶髷?shù)據(jù)量時(shí)容易造成性能問(wèn)題。以下是一些常見(jiàn)的優(yōu)化技巧:
- 避免使用
SELECT *:指定查詢(xún)字段,減少不必要的數(shù)據(jù)傳輸。 - 使用索引 :確保分頁(yè)字段(如主鍵或常用查詢(xún)字段)上有合適的索引。
- 避免深分頁(yè)問(wèn)題 :對(duì)于
LIMIT offset, size這種方式,當(dāng) offset 值很大時(shí)會(huì)導(dǎo)致性能下降,建議采用“游標(biāo)分頁(yè)”或“基于時(shí)間戳”的方式。 - 合理設(shè)置分頁(yè)大小 :避免一次請(qǐng)求返回過(guò)多數(shù)據(jù),建議每頁(yè) 10~50 條為宜。
示例優(yōu)化后的 SQL:
<select id="selectUsersByCondition" resultType="User">
SELECT id, username, email FROM users
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
</where>
</select>通過(guò)指定字段查詢(xún),減少數(shù)據(jù)庫(kù) IO 消耗,提升查詢(xún)效率。
4.3 分頁(yè)接口的測(cè)試與驗(yàn)證
4.3.1 單元測(cè)試的設(shè)計(jì)與執(zhí)行
為了確保分頁(yè)接口的正確性和穩(wěn)定性,必須進(jìn)行充分的單元測(cè)試??梢允褂?JUnit 框架結(jié)合 Spring Boot Test 進(jìn)行測(cè)試。
示例單元測(cè)試代碼如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private IUserMapper userMapper;
@Test
public void testSelectUsersWithPagination() {
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAllUsers();
Assert.notEmpty(users, "用戶(hù)列表不能為空");
Assert.isTrue(users.size() <= 10, "返回結(jié)果數(shù)量不應(yīng)超過(guò)每頁(yè)限制");
}
}該測(cè)試方法驗(yàn)證了分頁(yè)查詢(xún)是否返回了正確的數(shù)據(jù)量,并確保接口邏輯無(wú)誤。
4.3.2 分頁(yè)結(jié)果的驗(yàn)證與異常處理
在實(shí)際運(yùn)行中,可能會(huì)出現(xiàn)分頁(yè)失敗、SQL 錯(cuò)誤等情況。因此,在測(cè)試中還需驗(yàn)證異常處理邏輯。
例如,測(cè)試當(dāng)傳入非法頁(yè)碼時(shí)是否拋出異常:
@Test(expected = RuntimeException.class)
public void testInvalidPageNumber() {
PageHelper.startPage(-1, 10); // 無(wú)效頁(yè)碼
userMapper.selectAllUsers();
}此外,還可以通過(guò)日志記錄、異常捕獲等方式進(jìn)行異常處理,提升系統(tǒng)的健壯性。
附錄:分頁(yè)流程圖
以下是分頁(yè)查詢(xún)?cè)?MyBatis 中的完整執(zhí)行流程圖:
graph TD
A[調(diào)用Mapper方法] --> B{PageHelper是否啟動(dòng)}
B -->|是| C[攔截SQL并添加分頁(yè)語(yǔ)句]
C --> D[執(zhí)行SQL查詢(xún)]
D --> E[封裝結(jié)果為PageInfo]
B -->|否| F[直接執(zhí)行SQL查詢(xún)]
F --> G[返回原始結(jié)果]該流程圖清晰地展示了 PageHelper 在分頁(yè)查詢(xún)中的作用機(jī)制,有助于理解其工作原理。
小結(jié)
本章詳細(xì)介紹了在 MyBatis 中設(shè)計(jì) Mapper 接口進(jìn)行分頁(yè)查詢(xún)的方法,包括接口命名規(guī)范、PageHelper 的使用方式、動(dòng)態(tài) SQL 的編寫(xiě)與優(yōu)化技巧,以及接口的測(cè)試與異常處理。通過(guò)本章的學(xué)習(xí),開(kāi)發(fā)者可以掌握如何在實(shí)際項(xiàng)目中高效地實(shí)現(xiàn)分頁(yè)功能,并優(yōu)化其性能表現(xiàn)。
5. Service層分頁(yè)邏輯實(shí)現(xiàn)
在Spring Boot架構(gòu)中,Service層承擔(dān)著核心業(yè)務(wù)邏輯的處理職責(zé)。在分頁(yè)查詢(xún)場(chǎng)景下,Service層不僅要協(xié)調(diào)Mapper層的數(shù)據(jù)獲取,還需對(duì)分頁(yè)結(jié)果進(jìn)行封裝與處理。本章將從Service層的分頁(yè)邏輯設(shè)計(jì)、數(shù)據(jù)封裝方式、異常處理機(jī)制等方面深入探討,并通過(guò)代碼實(shí)例展示完整的實(shí)現(xiàn)流程。
5.1 Service層職責(zé)與分頁(yè)邏輯設(shè)計(jì)
Service層作為連接Controller與Mapper的橋梁,其設(shè)計(jì)直接影響分頁(yè)查詢(xún)的性能與可維護(hù)性。合理的邏輯設(shè)計(jì)能夠提高系統(tǒng)的可擴(kuò)展性與健壯性。
5.1.1 業(yè)務(wù)邏輯與分頁(yè)的結(jié)合方式
在企業(yè)級(jí)應(yīng)用中,分頁(yè)查詢(xún)往往不是單純的數(shù)據(jù)庫(kù)查詢(xún),而是需要結(jié)合業(yè)務(wù)規(guī)則進(jìn)行過(guò)濾、排序或聚合。例如,在用戶(hù)管理模塊中,可能需要根據(jù)角色、狀態(tài)等條件進(jìn)行篩選后再分頁(yè)。
public interface UserService {
PageInfo<UserDTO> getUsersByRoleAndStatus(String role, String status, int pageNum, int pageSize);
}在該接口中, role 和 status 是業(yè)務(wù)篩選條件, pageNum 和 pageSize 用于分頁(yè)控制。Service層通過(guò)組合這些參數(shù)調(diào)用Mapper層的查詢(xún)接口。
分頁(yè)邏輯流程圖(Mermaid格式)
graph TD
A[Controller層接收請(qǐng)求] --> B[調(diào)用Service層方法]
B --> C[Service層封裝參數(shù)]
C --> D[調(diào)用PageHelper.startPage()]
D --> E[執(zhí)行Mapper層查詢(xún)]
E --> F[封裝PageInfo對(duì)象]
F --> G[返回分頁(yè)結(jié)果]
5.1.2 調(diào)用Mapper接口實(shí)現(xiàn)分頁(yè)查詢(xún)
Service層調(diào)用Mapper接口前,需使用PageHelper插件開(kāi)啟分頁(yè)功能。以下是一個(gè)典型的調(diào)用示例:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public PageInfo<UserDTO> getUsersByRoleAndStatus(String role, String status, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<UserDTO> users = userMapper.selectByRoleAndStatus(role, status);
return new PageInfo<>(users);
}
}代碼逐行分析:
- 第7行 :注入U(xiǎn)serMapper,用于執(zhí)行數(shù)據(jù)庫(kù)查詢(xún)。
- 第11行 :調(diào)用
PageHelper.startPage()方法,指定當(dāng)前頁(yè)碼和每頁(yè)記錄數(shù)。 - 第12行 :調(diào)用Mapper接口的查詢(xún)方法,返回當(dāng)前頁(yè)的數(shù)據(jù)列表。
- 第13行 :使用MyBatis PageHelper提供的PageInfo類(lèi)封裝分頁(yè)信息,包括總記錄數(shù)、總頁(yè)數(shù)、當(dāng)前頁(yè)數(shù)據(jù)等。
5.2 分頁(yè)數(shù)據(jù)的處理與封裝
分頁(yè)查詢(xún)的結(jié)果通常包含當(dāng)前頁(yè)數(shù)據(jù)、總記錄數(shù)、頁(yè)碼信息等。Service層需對(duì)這些數(shù)據(jù)進(jìn)行封裝,并可能附加業(yè)務(wù)邏輯處理。
5.2.1 PageInfo對(duì)象的封裝過(guò)程
PageInfo是PageHelper提供的分頁(yè)信息封裝類(lèi),包含以下核心字段:
| 字段名 | 類(lèi)型 | 說(shuō)明 |
|---|---|---|
| pageNum | int | 當(dāng)前頁(yè)碼 |
| pageSize | int | 每頁(yè)記錄數(shù) |
| size | int | 當(dāng)前頁(yè)實(shí)際記錄數(shù) |
| total | long | 總記錄數(shù) |
| pages | int | 總頁(yè)數(shù) |
| list | List | 當(dāng)前頁(yè)的數(shù)據(jù)列表 |
| isFirstPage | boolean | 是否為第一頁(yè) |
| isLastPage | boolean | 是否為最后一頁(yè) |
封裝示例:
PageInfo<UserDTO> pageInfo = new PageInfo<>(users);
該語(yǔ)句自動(dòng)計(jì)算出總頁(yè)數(shù)、是否為首頁(yè)/尾頁(yè)等信息,開(kāi)發(fā)者可直接使用這些字段進(jìn)行頁(yè)面展示。
5.2.2 分頁(yè)數(shù)據(jù)的業(yè)務(wù)處理邏輯
在實(shí)際業(yè)務(wù)中,分頁(yè)結(jié)果可能需要進(jìn)行額外處理,例如:
- 數(shù)據(jù)轉(zhuǎn)換:將數(shù)據(jù)庫(kù)實(shí)體轉(zhuǎn)換為DTO(數(shù)據(jù)傳輸對(duì)象)。
- 數(shù)據(jù)脫敏:對(duì)敏感字段進(jìn)行隱藏或加密。
- 數(shù)據(jù)聚合:對(duì)查詢(xún)結(jié)果進(jìn)行統(tǒng)計(jì)或分組。
以下是一個(gè)帶有數(shù)據(jù)轉(zhuǎn)換與脫敏處理的示例:
@Override
public PageInfo<UserDTO> getUsersByRoleAndStatus(String role, String status, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectByRoleAndStatus(role, status);
// 數(shù)據(jù)轉(zhuǎn)換與脫敏
List<UserDTO> dtoList = users.stream()
.map(user -> {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setUsername(user.getUsername());
dto.setEmail("****@example.com"); // 脫敏處理
return dto;
})
.collect(Collectors.toList());
return new PageInfo<>(dtoList);
}
代碼邏輯說(shuō)明:
- 第6~13行 :將User實(shí)體轉(zhuǎn)換為UserDTO,并對(duì)email字段進(jìn)行脫敏處理。
- 第14行 :封裝為PageInfo對(duì)象,便于Controller層返回。
5.3 Service層的異常處理與日志記錄
在分頁(yè)操作中,可能出現(xiàn)SQL執(zhí)行異常、參數(shù)校驗(yàn)失敗、空指針等錯(cuò)誤。良好的異常處理機(jī)制與日志記錄策略有助于快速定位問(wèn)題。
5.3.1 分頁(yè)失敗的異常處理策略
建議使用Spring的全局異常處理器統(tǒng)一處理異常,以下為Service層的局部處理示例:
@Override
public PageInfo<UserDTO> getUsersByRoleAndStatus(String role, String status, int pageNum, int pageSize) {
try {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectByRoleAndStatus(role, status);
return new PageInfo<>(convertToDTO(users));
} catch (Exception e) {
// 捕獲異常并封裝為業(yè)務(wù)異常
throw new BusinessException("分頁(yè)查詢(xún)失敗:" + e.getMessage(), e);
}
}
異常處理流程圖(Mermaid格式)
graph TD
A[Service層執(zhí)行分頁(yè)] --> B{是否發(fā)生異常?}
B -->|是| C[捕獲異常]
C --> D[封裝為業(yè)務(wù)異常]
D --> E[拋出異常]
B -->|否| F[正常返回PageInfo]
5.3.2 日志記錄與問(wèn)題定位技巧
建議在Service層引入日志記錄器(如Logback、SLF4J),記錄關(guān)鍵參數(shù)和執(zhí)行結(jié)果:
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public PageInfo<UserDTO> getUsersByRoleAndStatus(String role, String status, int pageNum, int pageSize) {
logger.info("開(kāi)始分頁(yè)查詢(xún),角色:{}, 狀態(tài):{}, 當(dāng)前頁(yè):{}, 每頁(yè)記錄數(shù):{}", role, status, pageNum, pageSize);
try {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectByRoleAndStatus(role, status);
PageInfo<UserDTO> pageInfo = new PageInfo<>(convertToDTO(users));
logger.info("分頁(yè)查詢(xún)成功,總記錄數(shù):{}", pageInfo.getTotal());
return pageInfo;
} catch (Exception e) {
logger.error("分頁(yè)查詢(xún)失敗:", e);
throw new BusinessException("分頁(yè)查詢(xún)失?。? + e.getMessage(), e);
}
}日志輸出示例:
INFO UserServiceImpl - 開(kāi)始分頁(yè)查詢(xún),角色:admin, 狀態(tài):active, 當(dāng)前頁(yè):1, 每頁(yè)記錄數(shù):10 INFO UserServiceImpl - 分頁(yè)查詢(xún)成功,總記錄數(shù):50
日志記錄建議:
- 記錄請(qǐng)求參數(shù)、分頁(yè)參數(shù)、執(zhí)行結(jié)果等關(guān)鍵信息。
- 使用結(jié)構(gòu)化日志格式(如JSON)便于日志分析系統(tǒng)解析。
- 在異常發(fā)生時(shí),記錄堆棧信息以便快速定位問(wèn)題。
本章系統(tǒng)講解了Service層在分頁(yè)查詢(xún)中的核心職責(zé),包括分頁(yè)邏輯設(shè)計(jì)、數(shù)據(jù)封裝方式以及異常處理與日志記錄策略。通過(guò)代碼實(shí)例與流程圖的結(jié)合,展示了如何在實(shí)際開(kāi)發(fā)中高效實(shí)現(xiàn)分頁(yè)功能,并確保系統(tǒng)的穩(wěn)定性與可維護(hù)性。下一章將重點(diǎn)介紹Controller層如何接收分頁(yè)請(qǐng)求并返回統(tǒng)一格式的響應(yīng)。
6. Controller層接口接收與返回處理
在基于MyBatis和PageHelper構(gòu)建的分頁(yè)系統(tǒng)中,Controller層作為前后端交互的核心環(huán)節(jié),承擔(dān)著接收請(qǐng)求參數(shù)、調(diào)用業(yè)務(wù)邏輯并返回結(jié)構(gòu)化響應(yīng)的關(guān)鍵任務(wù)。本章將深入探討Controller層接口設(shè)計(jì)的規(guī)范、分頁(yè)數(shù)據(jù)的返回格式設(shè)計(jì),并結(jié)合實(shí)際代碼示例說(shuō)明如何通過(guò)接口測(cè)試工具(如Postman)進(jìn)行調(diào)試和問(wèn)題排查。
6.1 Controller層的接口設(shè)計(jì)原則
在Spring Boot框架中,Controller層主要通過(guò) @RestController 注解實(shí)現(xiàn),其設(shè)計(jì)需遵循RESTful風(fēng)格,同時(shí)兼顧接口的健壯性和可維護(hù)性。
6.1.1 接口路徑與請(qǐng)求方式的定義
接口路徑的設(shè)計(jì)應(yīng)遵循清晰、簡(jiǎn)潔的原則,通常以名詞復(fù)數(shù)形式表示資源集合。例如:
@GetMapping("/users")
public PageInfo<User> getUsers(@RequestParam int pageNum, @RequestParam int pageSize) {
return userService.getUsersByPage(pageNum, pageSize);
}
- 請(qǐng)求方式 :GET 用于獲取資源,POST 用于提交數(shù)據(jù)。
- 路徑命名 :
/users表示用戶(hù)資源,/orders表示訂單資源,遵循小寫(xiě)復(fù)數(shù)命名。 - 版本控制 :建議在URL中加入版本號(hào),如
/v1/users,便于后續(xù)接口升級(jí)。
6.1.2 請(qǐng)求參數(shù)的接收與校驗(yàn)機(jī)制
Controller層接收參數(shù)時(shí),應(yīng)進(jìn)行基本的校驗(yàn),防止非法輸入導(dǎo)致系統(tǒng)異常。Spring Boot提供了 @Valid 注解結(jié)合 javax.validation 實(shí)現(xiàn)參數(shù)校驗(yàn)。
示例:帶參數(shù)校驗(yàn)的分頁(yè)接口
@RestController
@RequestMapping("/v1/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public ResponseEntity<PageInfo<User>> getUsers(
@RequestParam @Min(1) int pageNum,
@RequestParam @Min(1) @Max(100) int pageSize) {
PageInfo<User> pageInfo = userService.getUsersByPage(pageNum, pageSize);
return ResponseEntity.ok(pageInfo);
}
}@Min(1):確保頁(yè)碼至少為1。@Max(100):限制每頁(yè)最多100條數(shù)據(jù),防止性能問(wèn)題。- 使用
ResponseEntity封裝響應(yīng),增強(qiáng)接口返回的可擴(kuò)展性。
6.2 分頁(yè)數(shù)據(jù)的返回格式設(shè)計(jì)
為了前后端交互的一致性和統(tǒng)一性,Controller層返回的分頁(yè)數(shù)據(jù)應(yīng)具備標(biāo)準(zhǔn)化結(jié)構(gòu),通常采用JSON格式,并封裝統(tǒng)一的響應(yīng)對(duì)象。
6.2.1 JSON格式的封裝與統(tǒng)一返回
建議使用統(tǒng)一的響應(yīng)封裝類(lèi) ResponseResult ,其結(jié)構(gòu)如下:
public class ResponseResult<T> {
private int code;
private String message;
private T data;
// 構(gòu)造方法、getters、setters
}
結(jié)合PageInfo返回:
@GetMapping
public ResponseResult<PageInfo<User>> getUsers(
@RequestParam @Min(1) int pageNum,
@RequestParam @Min(1) @Max(100) int pageSize) {
PageInfo<User> pageInfo = userService.getUsersByPage(pageNum, pageSize);
return ResponseResult.success(pageInfo);
}
響應(yīng)示例:
{
"code": 200,
"message": "操作成功",
"data": {
"pageNum": 1,
"pageSize": 10,
"total": 150,
"pages": 15,
"list": [
{"id": 1, "name": "張三", "email": "zhangsan@example.com"},
...
]
}
}code:狀態(tài)碼,如200表示成功。message:描述性信息,便于調(diào)試。data:封裝分頁(yè)數(shù)據(jù)對(duì)象PageInfo。
6.2.2 PageInfo對(duì)象在接口中的返回結(jié)構(gòu)
PageInfo 對(duì)象是PageHelper提供的分頁(yè)結(jié)果封裝類(lèi),包含以下核心字段:
| 字段名 | 類(lèi)型 | 描述 |
|---|---|---|
| pageNum | int | 當(dāng)前頁(yè)碼 |
| pageSize | int | 每頁(yè)數(shù)量 |
| total | long | 總記錄數(shù) |
| pages | int | 總頁(yè)數(shù) |
| list | List | 當(dāng)前頁(yè)的數(shù)據(jù)集合 |
| isFirstPage | boolean | 是否為首頁(yè) |
| isLastPage | boolean | 是否為尾頁(yè) |
mermaid流程圖:Controller層數(shù)據(jù)流向
graph TD
A[前端請(qǐng)求] --> B[Controller接收參數(shù)]
B --> C{參數(shù)校驗(yàn)是否通過(guò)?}
C -->|是| D[調(diào)用Service獲取PageInfo]
D --> E[封裝ResponseResult]
E --> F[返回JSON格式數(shù)據(jù)]
C -->|否| G[返回錯(cuò)誤信息]
6.3 接口測(cè)試與調(diào)試
接口開(kāi)發(fā)完成后,必須進(jìn)行充分的測(cè)試與調(diào)試,確保分頁(yè)邏輯正確、數(shù)據(jù)返回格式一致、異常處理得當(dāng)。
6.3.1 Postman測(cè)試接口的使用
使用Postman可以快速測(cè)試RESTful接口,以下是測(cè)試步驟:
- 打開(kāi)Postman ,新建請(qǐng)求。
- 設(shè)置請(qǐng)求方式 :GET。
- 輸入請(qǐng)求地址 :例如
http://localhost:8080/v1/users?pageNum=1&pageSize=10。 - 發(fā)送請(qǐng)求 ,查看返回的JSON結(jié)構(gòu)是否符合預(yù)期。
- 測(cè)試異常情況 :如
pageNum=0、pageSize=200,驗(yàn)證是否返回400錯(cuò)誤及提示信息。
示例截圖說(shuō)明(文字描述)
假設(shè)Postman界面左側(cè)為請(qǐng)求輸入?yún)^(qū)域,輸入U(xiǎn)RL和參數(shù)后點(diǎn)擊“Send”按鈕,右側(cè)顯示響應(yīng)結(jié)果。響應(yīng)體應(yīng)包含完整的PageInfo數(shù)據(jù)結(jié)構(gòu),并且狀態(tài)碼為200或400。
6.3.2 接口調(diào)用過(guò)程中的問(wèn)題排查
常見(jiàn)問(wèn)題及排查方法如下:
| 問(wèn)題現(xiàn)象 | 原因分析 | 解決方法 |
|---|---|---|
| 返回?cái)?shù)據(jù)為空 | SQL語(yǔ)句錯(cuò)誤、PageHelper未生效 | 檢查SQL是否執(zhí)行,日志輸出是否開(kāi)啟 |
| 分頁(yè)不準(zhǔn)確 | pageNum或pageSize參數(shù)傳遞錯(cuò)誤 | 添加參數(shù)校驗(yàn),確保參數(shù)合法 |
| 接口響應(yīng)超時(shí) | 數(shù)據(jù)量過(guò)大或SQL未優(yōu)化 | 檢查SQL執(zhí)行計(jì)劃,添加索引,優(yōu)化查詢(xún)語(yǔ)句 |
| PageInfo字段缺失或異常 | PageHelper版本兼容性問(wèn)題 | 升級(jí)PageHelper版本,或更換Spring Boot版本 |
示例:日志輸出輔助調(diào)試
在Spring Boot中啟用SQL日志輸出:
# application.yml
logging:
level:
com.example.mapper: debug輸出示例:
DEBUG c.e.m.UserMapper.getUsersByPage - ==> Preparing: SELECT * FROM users LIMIT 0,10 DEBUG c.e.m.UserMapper.getUsersByPage - ==> Parameters: DEBUG c.e.m.UserMapper.getUsersByPage - <== Total: 10
通過(guò)日志可以確認(rèn)分頁(yè)是否生效,SQL是否被PageHelper攔截并修改。
本章從Controller層接口設(shè)計(jì)原則出發(fā),深入講解了分頁(yè)接口的路徑定義、參數(shù)接收與校驗(yàn)機(jī)制,并詳細(xì)說(shuō)明了如何設(shè)計(jì)統(tǒng)一的JSON返回格式。通過(guò)引入 ResponseResult 與 PageInfo 對(duì)象,實(shí)現(xiàn)結(jié)構(gòu)化數(shù)據(jù)返回。最后,結(jié)合Postman測(cè)試與常見(jiàn)問(wèn)題排查技巧,幫助開(kāi)發(fā)者高效完成接口調(diào)試與問(wèn)題定位。
7. PageInfo對(duì)象使用詳解
7.1 PageInfo對(duì)象的結(jié)構(gòu)與屬性
PageInfo 是 PageHelper 提供的一個(gè)分頁(yè)數(shù)據(jù)封裝類(lèi),它封裝了當(dāng)前頁(yè)的數(shù)據(jù)信息以及與分頁(yè)相關(guān)的各種元數(shù)據(jù)。在使用 PageHelper 進(jìn)行分頁(yè)查詢(xún)后,通過(guò)調(diào)用 PageHelper.startPage() 方法后緊跟查詢(xún)語(yǔ)句,會(huì)自動(dòng)將結(jié)果封裝為 PageInfo 對(duì)象。
7.1.1 PageInfo類(lèi)的核心字段說(shuō)明
PageInfo 類(lèi)包含以下常用字段:
| 字段名 | 類(lèi)型 | 描述 |
|---|---|---|
| pageNum | int | 當(dāng)前頁(yè)碼 |
| pageSize | int | 每頁(yè)記錄數(shù) |
| size | int | 當(dāng)前頁(yè)的實(shí)際數(shù)據(jù)條數(shù) |
| startRow | int | 當(dāng)前頁(yè)第一個(gè)數(shù)據(jù)的行號(hào) |
| endRow | int | 當(dāng)前頁(yè)最后一個(gè)數(shù)據(jù)的行號(hào) |
| total | long | 總記錄數(shù) |
| pages | int | 總頁(yè)數(shù) |
| list | List<?> | 當(dāng)前頁(yè)的數(shù)據(jù)集合 |
| prePage | int | 上一頁(yè)頁(yè)碼 |
| nextPage | int | 下一頁(yè)頁(yè)碼 |
| isFirstPage | boolean | 是否為第一頁(yè) |
| isLastPage | boolean | 是否為最后一頁(yè) |
| hasPreviousPage | boolean | 是否存在上一頁(yè) |
| hasNextPage | boolean | 是否存在下一頁(yè) |
7.1.2 分頁(yè)信息的封裝過(guò)程
在 Service 層調(diào)用 Mapper 查詢(xún)后,PageHelper 會(huì)自動(dòng)將查詢(xún)結(jié)果封裝成 PageInfo 對(duì)象。例如:
PageHelper.startPage(pageNum, pageSize); // 設(shè)置分頁(yè)參數(shù) List<User> users = userMapper.selectAll(); // 執(zhí)行查詢(xún) PageInfo<User> pageInfo = new PageInfo<>(users); // 封裝成PageInfo
上述代碼中, PageHelper.startPage() 會(huì)攔截下一條 SQL 查詢(xún),并自動(dòng)添加分頁(yè)邏輯。查詢(xún)結(jié)果通過(guò) PageInfo 構(gòu)造函數(shù)進(jìn)行封裝,返回包含完整分頁(yè)信息的對(duì)象。
7.2 PageInfo在前端展示中的應(yīng)用
7.2.1 前端頁(yè)面獲取與解析PageInfo數(shù)據(jù)
在前后端分離架構(gòu)中,Controller 層通常會(huì)將 PageInfo 對(duì)象以 JSON 格式返回給前端。例如:
@GetMapping("/users")
public ResponseEntity<PageInfo<User>> getUsers(@RequestParam int pageNum, @RequestParam int pageSize) {
PageInfo<User> pageInfo = userService.getUsers(pageNum, pageSize);
return ResponseEntity.ok(pageInfo);
}
前端(如 Vue、React、Angular 等框架)接收到數(shù)據(jù)后,可以輕松解析并展示分頁(yè)信息:
fetch(`/api/users?pageNum=1&pageSize=10`)
.then(response => response.json())
.then(data => {
console.log('當(dāng)前頁(yè)碼:', data.pageNum);
console.log('總頁(yè)數(shù):', data.pages);
console.log('用戶(hù)列表:', data.list);
});
7.2.2 分頁(yè)控件的動(dòng)態(tài)渲染與交互邏輯
前端可以基于 PageInfo 中的 pageNum 、 pages 、 hasPreviousPage 和 hasNextPage 等字段來(lái)動(dòng)態(tài)渲染分頁(yè)控件,并實(shí)現(xiàn)跳轉(zhuǎn)邏輯。
例如,使用 Vue 渲染一個(gè)簡(jiǎn)單的分頁(yè)組件:
<template>
<div class="pagination">
<button :disabled="!pageInfo.hasPreviousPage" @click="prevPage">上一頁(yè)</button>
<span>當(dāng)前頁(yè):{{ pageInfo.pageNum }} / {{ pageInfo.pages }}</span>
<button :disabled="!pageInfo.hasNextPage" @click="nextPage">下一頁(yè)</button>
</div>
</template>
<script>
export default {
props: ['pageInfo'],
methods: {
prevPage() {
this.$emit('change-page', this.pageInfo.pageNum - 1);
},
nextPage() {
this.$emit('change-page', this.pageInfo.pageNum + 1);
}
}
}
</script>通過(guò)監(jiān)聽(tīng)按鈕點(diǎn)擊事件并重新請(qǐng)求對(duì)應(yīng)頁(yè)碼的數(shù)據(jù),實(shí)現(xiàn)頁(yè)面切換。
7.3 PageInfo的擴(kuò)展與自定義封裝
7.3.1 自定義分頁(yè)對(duì)象的封裝方式
雖然 PageInfo 已經(jīng)提供了豐富的字段,但在某些業(yè)務(wù)場(chǎng)景中,可能需要額外的字段或更靈活的數(shù)據(jù)結(jié)構(gòu)。此時(shí)可以自定義一個(gè)分頁(yè)響應(yīng)類(lèi)來(lái)封裝 PageInfo 的數(shù)據(jù)。
例如:
public class CustomPageResponse<T> {
private int currentPage;
private int pageSize;
private long totalElements;
private int totalPages;
private List<T> content;
private boolean hasNext;
private boolean hasPrevious;
public static <T> CustomPageResponse<T> fromPageInfo(PageInfo<T> pageInfo) {
CustomPageResponse<T> response = new CustomPageResponse<>();
response.setCurrentPage(pageInfo.getPageNum());
response.setPageSize(pageInfo.getPageSize());
response.setTotalElements(pageInfo.getTotal());
response.setTotalPages(pageInfo.getPages());
response.setContent(pageInfo.getList());
response.setHasNext(pageInfo.isHasNextPage());
response.setHasPrevious(pageInfo.isHasPreviousPage());
return response;
}
// Getters and Setters
}在 Controller 中返回該對(duì)象:
@GetMapping("/users")
public ResponseEntity<CustomPageResponse<User>> getUsers(@RequestParam int pageNum, @RequestParam int pageSize) {
PageInfo<User> pageInfo = userService.getUsers(pageNum, pageSize);
CustomPageResponse<User> response = CustomPageResponse.fromPageInfo(pageInfo);
return ResponseEntity.ok(response);
}
7.3.2 PageInfo與其他業(yè)務(wù)對(duì)象的整合使用
在實(shí)際業(yè)務(wù)中,分頁(yè)信息可能需要與其它數(shù)據(jù)(如統(tǒng)計(jì)數(shù)據(jù)、篩選條件、權(quán)限信息等)一起返回。此時(shí)可以將 PageInfo 作為響應(yīng)對(duì)象的一個(gè)字段進(jìn)行封裝。
例如:
public class UserPageResponse {
private PageInfo<User> pageInfo;
private int activeUserCount;
private int totalUserCount;
// 構(gòu)造方法、Getters、Setters
}
Controller 返回示例:
@GetMapping("/users")
public ResponseEntity<UserPageResponse> getUsers(@RequestParam int pageNum, @RequestParam int pageSize) {
PageInfo<User> pageInfo = userService.getUsers(pageNum, pageSize);
int activeCount = userService.getActiveUserCount();
int totalCount = pageInfo.getTotal();
UserPageResponse response = new UserPageResponse();
response.setPageInfo(pageInfo);
response.setActiveUserCount(activeCount);
response.setTotalUserCount(totalCount);
return ResponseEntity.ok(response);
}
這樣,前端可以在獲取分頁(yè)數(shù)據(jù)的同時(shí),也獲得其他業(yè)務(wù)信息,提升接口的靈活性和復(fù)用性。
到此這篇關(guān)于MyBatis分頁(yè)查詢(xún)實(shí)戰(zhàn)案例詳解的文章就介紹到這了,更多相關(guān)MyBatis分頁(yè)查詢(xún)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Plus進(jìn)行分頁(yè)查詢(xún)優(yōu)化的實(shí)踐指南
- Mybatisplus多表關(guān)聯(lián)分頁(yè)查詢(xún)多種實(shí)現(xiàn)方式
- 使用MyBatis-Plus實(shí)現(xiàn)聯(lián)表查詢(xún)分頁(yè)的示例代碼
- Mybatis?Plus分頁(yè)查詢(xún)返回total為0問(wèn)題解決
- MyBatis-Plus實(shí)現(xiàn)對(duì)查詢(xún)結(jié)果進(jìn)行分頁(yè)的基本步驟
- MybatisPlus多表查詢(xún)及分頁(yè)查詢(xún)完整代碼
- mybatis-plus分頁(yè)查詢(xún)的實(shí)現(xiàn)實(shí)例
- Springboot?Mybatis使用pageHelper如何實(shí)現(xiàn)分頁(yè)查詢(xún)
- MybatisPlus將自定義的sql列表查詢(xún)返回改為分頁(yè)查詢(xún)
相關(guān)文章
Java子線(xiàn)程無(wú)法獲取Attributes的解決方法(最新推薦)
在Java多線(xiàn)程編程中,子線(xiàn)程無(wú)法直接獲取主線(xiàn)程設(shè)置的Attributes是一個(gè)常見(jiàn)問(wèn)題,本文探討了這一問(wèn)題的原因,并提供了兩種解決方案,對(duì)Java子線(xiàn)程無(wú)法獲取Attributes的解決方案感興趣的朋友一起看看吧2025-01-01
springboot后端存儲(chǔ)富文本內(nèi)容的思路與步驟(含圖片內(nèi)容)
在所有的編輯器中,大概最受歡迎的就是富文本編輯器和MarkDown編輯器了,下面這篇文章主要給大家介紹了關(guān)于springboot后端存儲(chǔ)富文本內(nèi)容的思路與步驟的相關(guān)資料,需要的朋友可以參考下2023-04-04
springboot調(diào)用python腳本的實(shí)現(xiàn)示例
本文介紹了在SpringBoot應(yīng)用中調(diào)用Python腳本,包括ProcessBuilder類(lèi)和ApacheCommonsExec庫(kù)兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-12-12
基于java變量和作用域以及成員變量的默認(rèn)初始化(詳解)
下面小編就為大家介紹一下java變量和作用域以及成員變量的默認(rèn)初始化,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11
Java 編程如何使用 Class.forName() 加載類(lèi)
在一些應(yīng)用中,無(wú)法事先知道使用者將加載什么類(lèi),而必須讓使用者指定類(lèi)名稱(chēng)以加載類(lèi),可以使用 Class的靜態(tài)forName()方法實(shí)現(xiàn)動(dòng)態(tài)加載類(lèi),這篇文章主要介紹了Java編程如何使用Class.forName()加載類(lèi),需要的朋友可以參考下2022-06-06
Springboot整合Swagger2和Swagger3全過(guò)程
這篇文章主要介紹了Springboot整合Swagger2和Swagger3全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
@OneToMany查詢(xún)陷入循環(huán)引用的解決方案
這篇文章主要介紹了@OneToMany查詢(xún)陷入循環(huán)引用的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10

