你只是把项目里的一个依赖从旧版本升到新版本,代码一个字没动,重新构建时却直接挂掉,报错指向一个你从没手动引用过的"分页插件类"。翻遍了自己的业务代码也找不到它,可编译器就是咬定这个类不存在。
现象
构建日志里是这样的:
error: cannot find symbol
symbol: class PaginationPlugin
location: package com.example.orm.config
或者在运行启动时报类似 ClassNotFoundException。
诡异的地方在于:这个类和你的业务代码毫无关系,它是框架自带的分页插件,是你在配置文件里通过字符串或注解声明启用的。你甚至没在代码里 import 过它,只是照抄了一段"标准配置"。
根因
问题不在你的代码,而在依赖的大版本升级做了一次破坏性变更。
很多持久层框架/ORM 在早期版本里,分页是靠一个独立的"插件类"实现的——你把它注册进去,它负责在 SQL 执行前后自动拼接分页语句。到了新的大版本,框架把这类插件做了重构或移除:
- 有的把旧的
PaginationInterceptor改名成了新的PaginationInnerInterceptor; - 有的干脆把"自动分页"整个拿掉,让开发者自己处理分页参数;
- 有的把它挪到了别的包路径下。
你的配置文件里写的还是旧类名,而新版本的依赖里已经没有这个类了,于是编译或启动时就报"找不到类"。
这属于典型的破坏性变更(breaking change)。语义化版本里,主版本号变化就意味着"可能不兼容",但很多人在升级时只看依赖版本号,不看变更记录,于是被这类"看不见的引用"绊倒。
解决
思路:不再依赖这个被移除的插件类,改为显式地手写分页。
如果框架仍然提供替代类(比如改名后的新插件),最省事的做法是把配置里的旧类名换成新类名,行为基本不变。先到框架的官方文档或迁移指南里确认新类名。
如果框架彻底移除了自动分页,就把它改成手写 LIMIT / OFFSET:
SELECT * FROM article
ORDER BY created_at DESC
LIMIT 20 OFFSET 40; -- 第 3 页,每页 20 条
在代码里把"页码 page、每页条数 size"换算成 offset = (page - 1) * size,再传给 SQL。
注意两个容易再踩的细节:
- 必须带稳定的
ORDER BY。没有排序的分页,数据库返回顺序不保证,翻页时会出现重复或漏掉记录。 - 深翻页性能。
OFFSET很大时数据库仍要扫描并丢弃前面所有行,页数很深时改用基于游标的分页(记住上一页最后一条的 id/时间戳,用WHERE id > :last ORDER BY id LIMIT size)。
改动后,删掉配置里对旧插件类的引用,清一次构建缓存(比如 mvn clean、gradle clean、删掉 node_modules/.cache),再重新构建,确认那个"找不到类"的报错消失。
延伸与预防
这类坑的本质是:你的配置里藏着一份对框架内部实现的硬引用。 只要升级涉及"内部结构变动",这份引用就会断。
预防手段有三条:
- 升级前先看变更记录。找到依赖的 CHANGELOG 或 release notes,重点看
BREAKING、Deprecated、Removed这几个标记。主版本号 +1 时尤其不能跳过这一步。 - 锁定版本,别范围升级。依赖写死具体版本,避免构建时被动升到不兼容的大版本。
- 用自动化测试兜底。分页这种"看起来没变、行为其实变了"的逻辑,最需要一条会跑分页查询的测试用例守住。
一句话:编译报"找不到类",先怀疑是升级带来的移除,而不是你的代码写错了。