MyBatis-Plus進(jìn)行分頁(yè)查詢(xún)優(yōu)化的實(shí)踐指南
1. 引言
在實(shí)際開(kāi)發(fā)中,分頁(yè)查詢(xún)是常見(jiàn)的需求,尤其是需要關(guān)聯(lián)其他表獲取額外信息的場(chǎng)景。例如,在任務(wù)管理系統(tǒng)中,查詢(xún)?nèi)蝿?wù)列表時(shí),除了任務(wù)基本信息外,還需要顯示任務(wù)創(chuàng)建者的用戶名(通常存儲(chǔ)在用戶表中)。直接使用循環(huán)單條查詢(xún)會(huì)導(dǎo)致性能問(wèn)題,而合理的優(yōu)化可以顯著提升查詢(xún)效率。
本文將介紹如何使用 MyBatis-Plus 進(jìn)行高效分頁(yè)查詢(xún),并結(jié)合關(guān)聯(lián)查詢(xún)優(yōu)化數(shù)據(jù)填充,最終封裝成 VO(View Object) 返回給前端。我們將分析兩種優(yōu)化方案:
- 單條查詢(xún)填充(適用于數(shù)據(jù)量較小的情況)
- 批量查詢(xún)填充(適用于大數(shù)據(jù)量,提高性能)
2. 基礎(chǔ)分頁(yè)查詢(xún)實(shí)現(xiàn)
首先,我們回顧基礎(chǔ)的 MyBatis-Plus 分頁(yè)查詢(xún)實(shí)現(xiàn):
Page<TaskManagement> pageRequest = new Page<>(request.getPage(), request.getPageSize());
// 構(gòu)建查詢(xún)條件
LambdaQueryWrapper<TaskManagement> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(request.getStatus())) {
wrapper.like(TaskManagement::getStatus, request.getStatus());
}
// 執(zhí)行分頁(yè)查詢(xún)
IPage<TaskManagement> taskManagementIPage = taskManagementService.page(pageRequest, wrapper);
這段代碼完成了:
- 分頁(yè)參數(shù)構(gòu)建(
Page對(duì)象) - 動(dòng)態(tài)條件查詢(xún)(
LambdaQueryWrapper) - 執(zhí)行分頁(yè)查詢(xún)(
taskManagementService.page())
但返回的數(shù)據(jù)僅包含 TaskManagement 表的信息,缺少關(guān)聯(lián)的用戶名(creatorUsername),我們需要進(jìn)一步優(yōu)化。
3. 優(yōu)化方案1:?jiǎn)螚l查詢(xún)填充(適用于小數(shù)據(jù)量)
3.1 定義VO對(duì)象
首先,定義一個(gè) TaskManagementVO,增加 creatorUsername 字段:
@Data
public class TaskManagementVO {
private Long id;
private String taskName;
private String status;
// 其他任務(wù)字段...
private Long userId; // 創(chuàng)建者ID
private String creatorUsername; // 創(chuàng)建者姓名(需關(guān)聯(lián)用戶表查詢(xún))
}
3.2 轉(zhuǎn)換分頁(yè)數(shù)據(jù)并填充用戶名
使用 IPage.convert() 方法進(jìn)行數(shù)據(jù)轉(zhuǎn)換,并在轉(zhuǎn)換過(guò)程中查詢(xún)用戶名:
IPage<TaskManagementVO> voPage = taskManagementIPage.convert(task -> {
TaskManagementVO vo = new TaskManagementVO();
BeanUtils.copyProperties(task, vo); // 使用Spring BeanUtils復(fù)制屬性
if (task.getUserId() != null) {
// 查詢(xún)用戶信息
User user = userService.getById(task.getUserId());
if (user != null) {
vo.setCreatorUsername(user.getName());
}
}
return vo;
});
return voPage;
3.3 優(yōu)缺點(diǎn)分析
優(yōu)點(diǎn):
- 代碼簡(jiǎn)單,易于理解
- 適用于數(shù)據(jù)量較小的場(chǎng)景
缺點(diǎn):
- N+1查詢(xún)問(wèn)題:如果每頁(yè)有N條數(shù)據(jù),會(huì)額外執(zhí)行N次用戶查詢(xún),性能較差
- 不適合大數(shù)據(jù)量分頁(yè)
4. 優(yōu)化方案2:批量查詢(xún)填充(適用于大數(shù)據(jù)量)
4.1 優(yōu)化思路
- 先收集所有
userId - 批量查詢(xún)用戶數(shù)據(jù),減少數(shù)據(jù)庫(kù)交互
- 使用
Map存儲(chǔ)用戶信息,提高查詢(xún)效率
4.2 代碼實(shí)現(xiàn)
// 1. 提取所有不重復(fù)的userId
List<Long> userIds = taskManagementIPage.getRecords().stream()
.map(TaskManagement::getUserId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
// 2. 批量查詢(xún)用戶信息,并轉(zhuǎn)為Map<userId, User>
Map<Long, User> userMap = userIds.isEmpty()
? Collections.emptyMap()
: userService.listByIds(userIds).stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 3. 轉(zhuǎn)換分頁(yè)數(shù)據(jù)并填充用戶名
IPage<TaskManagementVO> voPage = taskManagementIPage.convert(task -> {
TaskManagementVO vo = new TaskManagementVO();
BeanUtils.copyProperties(task, vo);
if (task.getUserId() != null && userMap.containsKey(task.getUserId())) {
vo.setCreatorUsername(userMap.get(task.getUserId()).getName());
}
return vo;
});
return voPage;
4.3 優(yōu)化點(diǎn)分析
優(yōu)點(diǎn):
- 減少數(shù)據(jù)庫(kù)查詢(xún)次數(shù):無(wú)論分頁(yè)數(shù)據(jù)有多少條,用戶表只查詢(xún)1次
- 性能更高:適合大數(shù)據(jù)量場(chǎng)景
缺點(diǎn):
- 代碼稍復(fù)雜,需要額外處理
Map結(jié)構(gòu) - 如果
userIds過(guò)多(如 IN 查詢(xún)超過(guò)1000條),可能需要分批查詢(xún)
5. 進(jìn)一步優(yōu)化:使用JOIN查詢(xún)(SQL層面優(yōu)化)
如果 TaskManagement 和 User 表關(guān)聯(lián)頻繁,可以在 SQL 層面直接使用 JOIN 查詢(xún),避免多次查詢(xún):
5.1 自定義SQL查詢(xún)
<!-- TaskManagementMapper.xml -->
<select id="selectTaskPageWithUser" resultType="TaskManagementVO">
SELECT
t.*,
u.name AS creatorUsername
FROM
task_management t
LEFT JOIN
user u ON t.user_id = u.id
<where>
<if test="status != null and status != ''">
t.status LIKE CONCAT('%', #{status}, '%')
</if>
</where>
</select>
5.2 在Service層調(diào)用
// 使用自定義分頁(yè)查詢(xún)
IPage<TaskManagementVO> voPage = taskManagementMapper.selectTaskPageWithUser(
pageRequest,
request.getStatus());
5.3 優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 最高效:只需1次SQL查詢(xún)
- 適合復(fù)雜關(guān)聯(lián)查詢(xún)
缺點(diǎn):
- 需要手寫(xiě)SQL,不夠靈活
- 如果關(guān)聯(lián)表過(guò)多,SQL可能較復(fù)雜
6. 總結(jié)與最佳實(shí)踐
| 方案 | 適用場(chǎng)景 | 性能 | 代碼復(fù)雜度 |
|---|---|---|---|
| 單條查詢(xún)填充 | 數(shù)據(jù)量?。?lt;100條/頁(yè)) | 較差(N+1查詢(xún)) | 低 |
| 批量查詢(xún)填充 | 數(shù)據(jù)量較大(100-1000條/頁(yè)) | 較好(2次查詢(xún)) | 中 |
| JOIN查詢(xún) | 大數(shù)據(jù)量、頻繁關(guān)聯(lián)查詢(xún) | 最優(yōu)(1次查詢(xún)) | 高(需手寫(xiě)SQL) |
6.1 推薦選擇
- 小型項(xiàng)目/簡(jiǎn)單查詢(xún) → 單條查詢(xún)填充
- 中大型項(xiàng)目/數(shù)據(jù)量較大 → 批量查詢(xún)填充
- 高性能要求/復(fù)雜關(guān)聯(lián) → JOIN查詢(xún)
6.2 完整優(yōu)化代碼示例
public IPage<TaskManagementVO> getTaskPage(TaskPageRequest request) {
// 1. 構(gòu)建分頁(yè)查詢(xún)
Page<TaskManagement> pageRequest = new Page<>(request.getPage(), request.getPageSize());
LambdaQueryWrapper<TaskManagement> wrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(request.getStatus())) {
wrapper.like(TaskManagement::getStatus, request.getStatus());
}
// 2. 執(zhí)行分頁(yè)查詢(xún)
IPage<TaskManagement> taskManagementIPage = taskManagementService.page(pageRequest, wrapper);
// 3. 批量查詢(xún)用戶信息
List<Long> userIds = taskManagementIPage.getRecords().stream()
.map(TaskManagement::getUserId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
Map<Long, User> userMap = userIds.isEmpty()
? Collections.emptyMap()
: userService.listByIds(userIds).stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
// 4. 轉(zhuǎn)換VO并填充用戶名
return taskManagementIPage.convert(task -> {
TaskManagementVO vo = new TaskManagementVO();
BeanUtils.copyProperties(task, vo);
if (task.getUserId() != null && userMap.containsKey(task.getUserId())) {
vo.setCreatorUsername(userMap.get(task.getUserId()).getName());
}
return vo;
});
}
7. 結(jié)語(yǔ)
優(yōu)化分頁(yè)查詢(xún)的核心在于 減少數(shù)據(jù)庫(kù)交互次數(shù),本文介紹了三種優(yōu)化方案,適用于不同場(chǎng)景。在實(shí)際開(kāi)發(fā)中,應(yīng)根據(jù)業(yè)務(wù)需求和數(shù)據(jù)量選擇最合適的方案。
進(jìn)一步優(yōu)化建議:
- 使用 緩存(Redis) 存儲(chǔ)用戶信息,減少數(shù)據(jù)庫(kù)查詢(xún)
- 對(duì)于超大數(shù)據(jù)量(如10萬(wàn)+),考慮 游標(biāo)分頁(yè) 或 Elasticsearch 優(yōu)化
到此這篇關(guān)于MyBatis-Plus進(jìn)行分頁(yè)查詢(xún)優(yōu)化的實(shí)踐指南的文章就介紹到這了,更多相關(guān)mybatis-plus分頁(yè)查詢(xún)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis-Plus聯(lián)表查詢(xún)及分頁(yè)代碼舉例
- Mybatisplus多表關(guān)聯(lián)分頁(yè)查詢(xún)多種實(shí)現(xiàn)方式
- mybatisplus 多表關(guān)聯(lián)條件分頁(yè)查詢(xún)的實(shí)現(xiàn)
- mybatis-plus 實(shí)現(xiàn)分頁(yè)查詢(xún)的示例代碼
- 使用MyBatis-Plus實(shí)現(xiàn)聯(lián)表查詢(xún)分頁(yè)的示例代碼
- mybatis-plus分頁(yè)查詢(xún)的實(shí)現(xiàn)實(shí)例
- mybatis-plus分頁(yè)查詢(xún)?nèi)N方法小結(jié)
相關(guān)文章
Springboot+Bootstrap實(shí)現(xiàn)增刪改查實(shí)戰(zhàn)
這篇文章主要介紹了Springboot+Bootstrap實(shí)現(xiàn)增刪改查實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
基于request獲取訪問(wèn)者真實(shí)IP代碼示例
這篇文章主要介紹了基于request獲取訪問(wèn)者真實(shí)IP代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
Spring TaskScheduler使用實(shí)例解析
這篇文章主要介紹了Spring TaskScheduler使用實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
SpringBoot的單體和分布式的任務(wù)架構(gòu)詳解
在Spring Boot生態(tài)中,定時(shí)任務(wù)框架的選擇需根據(jù)架構(gòu)類(lèi)型(單體或分布式)和功能需求進(jìn)行權(quán)衡,以下從框架特性、適用場(chǎng)景及Spring Boot集成方式等角度,詳細(xì)梳理主流的定時(shí)任務(wù)框架及其分類(lèi),本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-05-05
Spring Boot 中的橫切邏輯統(tǒng)一治理方案
攔截器是Spring框架提供的核心功能之一,核心用于統(tǒng)一攔截用戶的請(qǐng)求,文章主要介紹了Spring框架中的攔截器、統(tǒng)一數(shù)據(jù)返回格式和統(tǒng)一異常處理,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2026-02-02
Spring如何消除代碼中的if-else/switch-case
這篇文章主要給大家介紹了關(guān)于Spring如何消除代碼中if-else/switch-case的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Java多線程run方法中直接調(diào)用service業(yè)務(wù)類(lèi)應(yīng)注意的問(wèn)題及解決
這篇文章主要介紹了Java多線程run方法中直接調(diào)用service業(yè)務(wù)類(lèi)應(yīng)注意的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
java實(shí)現(xiàn)對(duì)map的字典序排序操作示例
這篇文章主要介紹了java實(shí)現(xiàn)對(duì)map的字典序排序操作,結(jié)合實(shí)例形式分析了java參照微信官網(wǎng)算法實(shí)現(xiàn)的字典序排序操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-07-07
Spring中事務(wù)幾個(gè)常見(jiàn)的問(wèn)題解決
這篇文章主要介紹了Spring中事務(wù)幾個(gè)常見(jiàn)的問(wèn)題解決,事務(wù)這個(gè)概念是數(shù)據(jù)庫(kù)層面的,Spring只是基于數(shù)據(jù)庫(kù)中的事務(wù)進(jìn)行擴(kuò)展,以及提供了一些能讓程序員更新方便操作事務(wù)的方式2022-08-08
一文盤(pán)點(diǎn)Spring MVC中用于處理HTTP請(qǐng)求的常用注解
這篇文章主要為大家詳細(xì)介紹了Spring MVC中用于處理HTTP請(qǐng)求的常用注解,包括@RequestMapping,@RequestController,@RequestParam等,希望對(duì)大家有所幫助2026-03-03

