在数据库的世界里,分页实际上就是一场关于“截取”的博弈。想象一下,你手里有一大叠纸,想从中切出一小块来给阅读者看,这时候你手里有两把刀:一把是逻辑上的定位,一把是物理上的切割。MyBatis-Plus 默认用的是那把逻辑定位的刀,也就是 `offset` 和 `limit`。你不需求关心具体的索引页码是多少,也不用去算 `(size - 1) offset + 1` 这种复杂的公式,你只需求告诉数据库:“从第几页启动,取多少条”。 这就好比你去图书馆借书,你不会出于只想借一本《哈利波特》而非要翻到第 500 页去,你只需求说:“我要借第一本”,然后系统直接去书架上对应的位置给你。MyBatis-Plus 默认就是干这个的,它把 `first` 当作第几页,`size` 当作这一页要拿几本,利用 B 树索引这种底层数据结构,直接把你要的数据剖出来扔给你。 这背后的原理实际上挺省心的,它利用了数据库的自动优化。当你设置 `limit` 时,数据库不会像那会儿那样从第 1 页启动顺藤摸瓜,而是直接跳到第 `first` 页。

要是 `first` 正好是一个分块索引(比如 1 页 10 条),它就直接切;要是 `first` 是个中间页,比如 100 页,系统就在那里构造一个跳板点,直接扣掉 100 条。

这个过程不需求你写一堆 `in` 语句要么 `between` 逻辑去硬控,它自己就会在内部把那些无涉的页全给过滤掉,只留下你圈出来的那局部。 这就好比你在画图软件里画了一个矩形,系统自动帮你把周围留着的局部裁掉了,只把你要画的局部切出来。

只要你在查询条件里加上 `is_active = 1`,要么加上 `status` 字段,那些没激活的数据就已经被系统悄悄剔除在外,再也进不了你的视野。

要是只给 `limit` 不给 `first`,那它默认就是从第 1 页启动往下数,直到数够 `size` 条为止,这时候 `limit` 就失效了,出于它只是负责收尾的活儿,真正的起点还得靠 `first` 来定。 实际上大量人认定 MyBatis-Plus 这种分页忒“懒”了,仿佛彻底没做那点事,实际上不然。它只是把最繁琐的数学计算和索引跳跃逻辑外包给了数据库引擎,咱们开发者只管写 SQL 看数据就行。 举个例子,咱们假设有个用户表,里面有 10 万条记录。

要是咱们直接不想用分页逻辑,而是硬要用 `count` 和 `left join` 去找第 N 页的数据,那把 SQL 写出来得跟数学题似的,还得寻思各种边界条件,万一数据量大了,效率直接腰斩。MyBatis-Plus 自然是如此干的,它根本不用你操心这些坑。你只需求写一行代码,就连都不用改参数,它就能自动帮你生成那种“先判空、再查询、后翻页”的魔法 SQL。 在写代码的时候,你会发现它的行为有时候挺随性的。

比如你设置 `pageSize` 为 10,要是 `first` 是 1,那它直接从第 1 条启动取 10 条,哪怕这一页只有 5 条,它也会把剩下的 5 条取走,这就造成了“截断”的情况。但要是 `first` 是 100,而 `limit` 是 99,它只会取最终 99 条,不会多取。

这种对数值的灵活管住,正是它好用的一局部。 有时候你就连希望它像那种“贪心”的家伙,不管页码大小,只管取指定数量。

比如你只想看最近 20 条最新的,不管历史数据有多少。

这时候 `first` 能够设为 0,`limit` 设为 20。

这时候不管前面有多少页,它都直接取 0 到 20 的记录,彻底无视前面的那些页。别看这可能不是最推荐的架构(出于好办造成数据倾斜),但在追求极致性能的时候,这种“暴力截取”有时候也挺诱人。 再看 `offset` 和 `limit` 之间的关系,那简直就是一场 doublespeak 的闹剧。在 SQL 里,当你写 `LIMIT 10 OFFSET 5` 时,在 MyBatis-Plus 的视角里,这实际上是一个形而上的概念:`first` 变成了 6,`size` 变成了 10。你不用关心它内部是不是先减了 5 再加了 10。它只认这个格式。

要是 `first` 是 12,`limit` 是 10,那 `OFFSET` 就不起功能了,出于数据库已经直接给你切掉了前 12 条,剩下的自然就是 0 到了 9 这 10 条,不需求再去偏移了。 还有一个细节挺好办被忽略,就是 `offset` 和 `limit` 的组合逻辑。

要是 `offset` 大于 0,`limit` 务必大于 0,并且 `offset + limit` 不能超过总数。

要是两者都大于 0,那这就像是你在断链里切了一块肉,你既想跳过前面还能切下一点。

这时候系统会先计算前 `offset` 条,然后算出剩余条数,最终再切 `limit` 条。

要是你只给了 `offset` 没给 `limit`,那它默认 `limit` 等于剩余条数,这实际上是个挺智慧的默认值,既知足了翻页的需求,又保证了数据的整个性。 在复杂的业务场景里,比如你要分页查订单,顺便统计某个订单的积分,这时候分页逻辑就变成了一种数据维度的切片。你不需求知道所有订单的总数,直接把 `count` 条件放进来,然后加上分页限制,数据库就能自动帮你把“分页”和“统计”这两条线缝合在一起。

这种灵活性在 MyBatis-Plus 里显得尤为惊人,出于它把原本需求多写几行 SQL、就连要维护多个索引的复杂逻辑,简化成了单纯的数据截取操作。 自然,不同的场景下,这种“截取”策略也是有益处的。在读取大量历史数据归档时,要是每次只读最近的一页,那么基于索引的跳跃路径会贼短,查询速度极快。而在需求遍历所有数据时,分页带来的索引跳跃优势就体现得淋漓尽致。 在实际开发中,我们往往更关心 `first` 和 `size` 这两个参数的交互。`limit` 这个参数实际上大量时候是个红鲱鱼,它是为了配合 `offset` 存有的。真正的管住点一直在 `first` 和 `size` 的粒度上。

要是你想要管住每一页的数量,那就只改 `size`;要是你想要管住跨页的总跨度,那就只改 `first` 和 `limit` 的组合。MyBatis-Plus 智慧地避开了那些低效的中间变量,直接保留了最核心的管住逻辑。 最终说句大白话,别看 MyBatis-Plus 的默认分页逻辑已经挺复杂了,但在大多数情况下,它确实比手写 SQL 要省时省力多了。它就像是一个懂行情的 AI 助手,你只需求说“我要看第 3 页,每页 5 条”,它自己就会在后台把那些脏数据剔除,把干净利落的切片传给你。

不用管它内部是不是用了 B 树、用了跳点、用了索引、用了自动优化,你只管拿数据。

这种把复杂逻辑封装成好办参数的做法,正是现代框架设计的精髓所在,也让咱们开发者能从繁琐的索引计算和 SQL 拼接中解脱出来,专注于真正需求思索的业务逻辑。