最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis-Plus分頁失效問題的完整解決方案

 更新時間:2026年06月24日 08:57:19   作者:霸道流氓氣質(zhì)  
本文詳細(xì)分析了使用PageHelper與MyBatis-Plus分頁插件時的沖突原因,并提供了正確使用MyBatis-Plus分頁的方法,幫助開發(fā)者避免常見問題,需要的朋友可以參考下

一、問題描述

使用 PageHelper.startPage() 配合 MyBatis-Plus 的 BaseMapper.selectList(Wrapper) 進(jìn)行分頁查詢時,實(shí)際執(zhí)行的 SQL 沒有 LIMIT 子句,返回了全部數(shù)據(jù)。

// 期望生成:SELECT * FROM table WHERE ... LIMIT 0, 50
// 實(shí)際生成:SELECT * FROM table WHERE ...(無分頁)
PageHelper.startPage(1, 50);
List<Entity> list = mapper.selectList(wrapper);

二、兩種分頁方案對比

2.1 PageHelper(第三方分頁插件)

  • 來源:com.github.pagehelper:pagehelper
  • 原理:通過 MyBatis Interceptor 攔截 SQL,自動追加 LIMIT
  • 配合對象:PageInfo 包裝分頁結(jié)果
PageHelper.startPage(pageNum, pageSize);
List<User> list = userMapper.selectByCondition(param);
PageInfo<User> pageInfo = new PageInfo<>(list);

2.2 MyBatis-Plus 內(nèi)置分頁

  • 來源:com.baomidou:mybatis-plus 內(nèi)置
  • 原理:通過 PaginationInnerInterceptor 攔截 SQL
  • 配合對象:Page<T> 作為參數(shù)傳入
Page<User> page = new Page<>(pageNum, pageSize);
Page<User> result = userMapper.selectPage(page, wrapper);

三、沖突原因深度分析

3.1 PageHelper 的工作原理

① PageHelper.startPage(1, 50)
   → 將分頁參數(shù)存入 ThreadLocal

② mapper.selectXxx()
   → MyBatis 執(zhí)行 SQL 前,PageHelper 的 Interceptor 檢查 ThreadLocal
   → 發(fā)現(xiàn)有分頁參數(shù),改寫 SQL 追加 LIMIT
   → 執(zhí)行完畢后,清除 ThreadLocal 中的分頁參數(shù)

③ new PageInfo<>(list)
   → 從 list(被 PageHelper 包裝過的 Page 對象)中取分頁信息

關(guān)鍵點(diǎn):PageHelper 只會攔截 startPage() 之后的下一條 SQL 執(zhí)行。

3.2 為什么與 MyBatis-Plus 的 selectList 沖突

MyBatis-Plus 的 BaseMapper.selectList(Wrapper) 不是簡單的一次 SQL 調(diào)用。其內(nèi)部流程:

mapper.selectList(wrapper)
  ↓
MyBatis-Plus 動態(tài)構(gòu)建 SQL(通過 AbstractWrapper 生成 WHERE 條件)
  ↓
調(diào)用 MyBatis 的 SqlSession.selectList()
  ↓
MyBatis Executor 執(zhí)行

沖突發(fā)生的幾種可能原因:

原因說明
Interceptor 執(zhí)行順序PageHelper 的 Interceptor 和 MyBatis-Plus 的 Interceptor 注冊順序不當(dāng),導(dǎo)致 PageHelper 沒有正確攔截到最終的 SQL
ThreadLocal 被提前消費(fèi)如果在 startPage()selectList() 之間有其他 SQL 執(zhí)行(如條件構(gòu)造時觸發(fā)了查詢),分頁參數(shù)已被消費(fèi)
SQL 改寫時機(jī)不匹配MyBatis-Plus 構(gòu)造的 SQL 在 PageHelper 攔截時還未最終確定,導(dǎo)致 LIMIT 沒有追加到正確的位置
版本兼容性不同版本的 PageHelper 和 MyBatis-Plus 對 Interceptor SPI 的實(shí)現(xiàn)有差異

3.3 PageHelper 的 ThreadLocal 機(jī)制詳解

public class PageHelper {
    // 每個線程獨(dú)立的分頁參數(shù)
    private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<>();

    public static <E> Page<E> startPage(int pageNum, int pageSize) {
        Page<E> page = new Page<>(pageNum, pageSize);
        LOCAL_PAGE.set(page);  // 存入當(dāng)前線程
        return page;
    }
}

// Interceptor 中:
public class PageInterceptor implements Interceptor {
    public Object intercept(Invocation invocation) {
        Page page = PageHelper.LOCAL_PAGE.get();
        if (page != null) {
            // 改寫 SQL 追加 LIMIT
            PageHelper.LOCAL_PAGE.remove();  // 用完立即清除
            // ...執(zhí)行帶 LIMIT 的 SQL
        }
        return invocation.proceed();
    }
}

問題核心:如果 LOCAL_PAGE.get() 在錯誤的時機(jī)被調(diào)用(SQL 還沒準(zhǔn)備好),或者在正確時機(jī)沒被調(diào)用(Interceptor 順序靠后),分頁就失效。

四、MyBatis-Plus 分頁的正確用法

4.1 注冊分頁插件(必須)

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        PaginationInnerInterceptor pagination = new PaginationInnerInterceptor(DbType.MYSQL);
        pagination.setMaxLimit(500L); // 單頁最大條數(shù)限制
        interceptor.addInnerInterceptor(pagination);
        return interceptor;
    }
}

沒有這個配置,selectPage 方法的分頁參數(shù)會被忽略,等同于 selectList

4.2 使用 selectPage

// 創(chuàng)建分頁對象(頁碼從1開始)
Page<Entity> page = new Page<>(pageNum, pageSize);

// 執(zhí)行分頁查詢
Page<Entity> result = mapper.selectPage(page, wrapper);

// 獲取結(jié)果
List<Entity> records = result.getRecords();  // 當(dāng)前頁數(shù)據(jù)
long total = result.getTotal();              // 總記錄數(shù)
long current = result.getCurrent();          // 當(dāng)前頁碼
long size = result.getSize();                // 每頁條數(shù)
long pages = result.getPages();              // 總頁數(shù)

4.3 生成的 SQL

-- MyBatis-Plus 分頁實(shí)際執(zhí)行兩條 SQL:
-- 第一條:查總數(shù)
SELECT COUNT(*) AS total FROM user WHERE status = 1
-- 第二條:查數(shù)據(jù)(帶 LIMIT)
SELECT id, name, status FROM user WHERE status = 1 LIMIT 0, 50

4.4 優(yōu)化:不需要總數(shù)時跳過 COUNT

// 第三個參數(shù) false 表示不查詢總數(shù)(提升性能)
Page<Entity> page = new Page<>(pageNum, pageSize, false);

五、PageHelper 和 MyBatis-Plus 分頁能否共存

可以共存,但需要明確分工:

場景推薦方案
使用 MyBatis-Plus 的 BaseMapper 方法(selectList/selectPage)用 MyBatis-Plus 分頁
使用自定義 XML 中的 SQL(如復(fù)雜 JOIN 查詢)用 PageHelper 或 MyBatis-Plus 分頁都可以
混合使用按方法區(qū)分,不要在同一方法中混用

原則:一個查詢方法中只使用一種分頁方式,不要混用。

六、完整示例

6.1 Entity

package com.example.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.util.Date;
import lombok.Data;

@Data
@TableName("product")
public class Product {
  @TableId(type = IdType.AUTO)
  private Long id;
  private String code;
  private String name;
  private String category;
  private Integer price;
  private Integer status;
  private Date createTime;
}

6.2 Mapper

package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Product;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}

6.3 分頁插件配置

package com.example.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

  @Bean
  public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

    PaginationInnerInterceptor pagination = new PaginationInnerInterceptor(DbType.MYSQL);
    pagination.setMaxLimit(500L);    // 防止惡意大分頁
    pagination.setOverflow(false);   // 頁碼超出總頁數(shù)時不自動回到第一頁

    interceptor.addInnerInterceptor(pagination);
    return interceptor;
  }
}

6.4 Service

package com.example.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.entity.Product;
import com.example.mapper.ProductMapper;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

  @Resource
  private ProductMapper productMapper;

  /**
   * 分頁查詢商品列表.
   *
   * @param name     商品名稱(模糊搜索)
   * @param category 分類(精準(zhǔn)搜索)
   * @param status   狀態(tài)
   * @param pageNum  頁碼(從1開始)
   * @param pageSize 每頁條數(shù)
   * @return 分頁結(jié)果
   */
  public Map<String, Object> listProducts(
      String name, String category, Integer status,
      int pageNum, int pageSize) {

    // 1. 構(gòu)造查詢條件
    LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
    wrapper.like(name != null && !name.isBlank(), Product::getName, name);
    wrapper.eq(category != null && !category.isBlank(), Product::getCategory, category);
    wrapper.eq(status != null, Product::getStatus, status);
    wrapper.orderByDesc(Product::getCreateTime);

    // 2. 創(chuàng)建分頁對象
    Page<Product> page = new Page<>(pageNum, pageSize);

    // 3. 執(zhí)行分頁查詢(會自動執(zhí)行 COUNT + SELECT LIMIT)
    Page<Product> pageResult = productMapper.selectPage(page, wrapper);

    // 4. 組裝返回結(jié)果
    Map<String, Object> result = new HashMap<>();
    result.put("list", pageResult.getRecords());     // 當(dāng)前頁數(shù)據(jù)
    result.put("total", pageResult.getTotal());      // 總記錄數(shù)
    result.put("pageNum", pageResult.getCurrent());  // 當(dāng)前頁碼
    result.put("pageSize", pageResult.getSize());    // 每頁條數(shù)
    result.put("pages", pageResult.getPages());      // 總頁數(shù)
    return result;
  }
}

6.5 Controller

package com.example.controller;

import com.example.service.ProductService;
import jakarta.annotation.Resource;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/product")
public class ProductController {

  @Resource
  private ProductService productService;

  @GetMapping("/list")
  public Map<String, Object> list(
      @RequestParam(required = false) String name,
      @RequestParam(required = false) String category,
      @RequestParam(required = false) Integer status,
      @RequestParam(defaultValue = "1") int pageNum,
      @RequestParam(defaultValue = "10") int pageSize) {
    return productService.listProducts(name, category, status, pageNum, pageSize);
  }
}

6.6 執(zhí)行效果

請求:

GET /api/product/list?category=電子產(chǎn)品&pageNum=2&pageSize=10

實(shí)際執(zhí)行的 SQL(可在日志中看到):

-- 第一條:查總數(shù)
SELECT COUNT(*) AS total FROM product WHERE category = '電子產(chǎn)品'
-- 第二條:查數(shù)據(jù)
SELECT id, code, name, category, price, status, create_time
FROM product
WHERE category = '電子產(chǎn)品'
ORDER BY create_time DESC
LIMIT 10, 10

返回:

{
  "list": [...],
  "total": 156,
  "pageNum": 2,
  "pageSize": 10,
  "pages": 16
}

七、PaginationInnerInterceptor 配置項(xiàng)

配置項(xiàng)默認(rèn)值說明
dbType必填數(shù)據(jù)庫類型,影響 LIMIT 語法生成
maxLimit無限制單頁最大條數(shù)(防止 pageSize=999999 惡意請求)
overflowfalse頁碼超出時:true=自動回第一頁,false=返回空數(shù)據(jù)
optimizeJointrue優(yōu)化 COUNT SQL(去掉不必要的 JOIN 和 ORDER BY)

八、常見問題排查清單

現(xiàn)象可能原因解決方案
SQL 完全沒有 LIMIT沒注冊 PaginationInnerInterceptor添加 MybatisPlusConfig 配置類
total 始終為 0數(shù)據(jù)庫方言設(shè)置錯誤確認(rèn) DbType.MYSQL 與實(shí)際數(shù)據(jù)庫匹配
返回全部數(shù)據(jù)用了 selectList 而非 selectPage改用 selectPage 并傳入 Page 對象
PageHelper 時好時壞startPage 和查詢之間有其他 SQL確保 startPage 緊跟查詢,或改用 MyBatis-Plus 分頁
分頁參數(shù)被忽略Page 對象沒傳給 selectPage檢查方法簽名,確認(rèn) page 是第一個參數(shù)

九、總結(jié)

選型建議:

使用 MyBatis-Plus 的項(xiàng)目 → 優(yōu)先用 MyBatis-Plus 內(nèi)置分頁(selectPage + PaginationInnerInterceptor)
? 與 MyBatis-Plus 的 Wrapper 體系天然兼容
? 不依賴 ThreadLocal,無執(zhí)行順序隱患
? 支持多數(shù)據(jù)庫方言

使用原生 MyBatis + XML 的項(xiàng)目 → 用 PageHelper
? 對已有代碼侵入小
? 不需要修改 Mapper 方法簽名

混合項(xiàng)目 → 不要在同一個方法中混用兩種分頁

以上就是MyBatis-Plus分頁失效問題的完整解決方案的詳細(xì)內(nèi)容,更多關(guān)于MyBatis-Plus分頁失效問題的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

禹州市| 莫力| 渝北区| 建宁县| 沅陵县| 定安县| 简阳市| 大邑县| 涟源市| 淮阳县| 德化县| 玉山县| 阳江市| 界首市| 桐乡市| 新沂市| 西贡区| 内江市| 同江市| 航空| 丽江市| 柳江县| 长丰县| 城固县| 宝丰县| 张家口市| 靖远县| 英吉沙县| 治县。| 嘉荫县| 牟定县| 襄垣县| 虞城县| 久治县| 安图县| 宜川县| 南靖县| 昭觉县| 静海县| 连云港市| 昌都县|