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

