MyBatis-Flex 与 MyBatis-Plus 的核心差异在设计路线:MyBatis-Plus 走「在 MyBatis 之上尽量多扩展」,靠拦截器和 SQL 解析叠加功能;MyBatis-Flex 走「极简轻量」,零拦截器、零 SQL 解析、除 MyBatis 外零第三方依赖,条件查询直接拼 SQL。做 AI 爆款文章创作器时我把数据层从 MyBatis-Plus 换成了 MyBatis-Flex,原因是它查询单条数据速度大约是 MyBatis-Plus 的 5 到 10 倍,QueryWrapper 原生支持多表 join,复合主键和数据脱敏、字段加密这些能力免费开放。下面把两条路线的差异、性能来源、迁移过程和实际用法说清楚,帮你做选型时心里有底。
一、两条设计路线决定了本质差异
MyBatis-Plus 的 SQL 生成发生在启动期,通过注入 MappedStatement 实现,分页、多租户、逻辑删除都依赖拦截器,拦截器内部还要解析原始 SQL。MyBatis-Flex 把 SQL 生成放到运行时,靠 Provider 注解动态生成,分页直接内建在 core 模块,QueryWrapper 支持跨表 join,不需要 XML,也不解析 SQL。
| 对比维度 | MyBatis-Plus 3.x | MyBatis-Flex |
|---|---|---|
| SQL 生成方式 | 启动期注入 MappedStatement | 运行时 Provider 注解 |
| 拦截器依赖 | 分页、租户等功能依赖拦截器 | 无拦截器 |
| SQL 解析 | 拦截器内解析原始 SQL | 不解析,直接拼 SQL |
| 第三方依赖 | core + extension + starter | 只依赖 MyBatis |
| 多表查询 | 需要手写 XML | QueryWrapper 直接 join |
| 复合主键 | 不支持 | 支持 |
| 数据脱敏/字段加密 | 收费功能 | 免费 |
| QueryWrapper 序列化 | 不支持 | 支持 RPC 传输 |
选型时盯着这张表看就够了:多表查询多、想要轻量、不想为脱敏加密付费,选 Flex;团队对 Plus 生态熟、文档依赖强、老项目维护,继续用 Plus 更稳妥。
二、性能差异来自哪里
官方基准测试显示,MyBatis-Flex 的单条查询、十条查询、分页查询和数据更新的速度都大约是 MyBatis-Plus 的 5 到 10 倍。差距不是玄学,来源有三处。第一,Plus 的拦截器在每次执行时都要解析 SQL,解析本身就是开销;Flex 不解析,条件直接拼进 SQL。第二,Flex 分页用 APT 在编译期生成 QueryColumn 常量,条件类型安全且零反射成本。第三,Flex 的内核只有 MyBatis 一个依赖,类加载和初始化负担小。
2.1 APT 生成的类型安全条件
Flex 的查询条件列是编译期常量,不是运行时字符串。实体类经过 APT 处理后生成 ARTICLE 这种静态列引用,写错字段名编译直接报错,重构字段时 IDE 全局联动,比字符串形式的列名可靠得多。这一点在文章创作器项目里体会很深,文章表和执行日志表字段调整过几次,编译期就把引用错误拦下来了,没让脏 SQL 上线。
2.2 分页内置在 core
Plus 的分页要额外配 PaginationInnerInterceptor,Flex 的分页能力直接内建,不需要额外配置。项目里文章列表分页、执行日志翻页都是 paginate 一步到位,少了一层配置,也少了一个拦截器处理环节。
Page<ArticleRow> page = Db.paginate("article",
Page.of(pageNum, pageSize),
QueryWrapper.create().where(ARTICLE.USER_ID.eq(userId))
.orderBy(ARTICLE.CREATE_TIME.desc()));
三、项目里从 Plus 迁移到 Flex 的过程
迁移不是推翻重写,按三步走,风险可控:
- 替换依赖:
mybatis-plus-boot-starter换成mybatis-flex-spring-boot3-starter,版本跟随 Spring Boot 大版本选对应 starter; - 改查询层:Service 里原 Plus 的
LambdaQueryWrapper换成 Flex 的QueryWrapper,列引用从 Lambda 方法引用换成 APT 生成的常量; - 保留 Mapper:基础 CRUD 和原生 SQL 的 Mapper 写法两边兼容,XML 文件不需要动。
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
<version>1.7.9</version>
</dependency>
迁移完成后,文章表、执行日志表的查询全部走新框架,期间旧 Mapper 方法并行保留,逐模块验证通过后再清理。执行日志这种高频写表路径的吞吐量提升最明显,批量写入从几十毫秒级降到个位数毫秒级。整体迁移花了两个迭代,验证通过前新旧并存,回滚路径是现成的,出问题把依赖换回去即可。
四、实际用法里的几个亮点
用 Db + Row 处理无实体类的轻量查询非常顺手,不用为每张表都建实体。比如统计每个用户生成了多少篇文章:
List<Row> rows = Db.selectListByQuery("article", QueryWrapper.create()
.select(ARTICLE.USER_ID.as("userId"), count(ARTICLE.ID).as("cnt"))
.groupBy(ARTICLE.USER_ID)
.orderBy("cnt", false));
条件为 null 时自动忽略,写查询不用手动判空。多表场景 QueryWrapper 直接 join,比如文章表关联用户表取昵称,一条链式调用完成,不用回 XML。数据脱敏、字段加密在 Plus 里是收费模块,Flex 里免费,这正是项目敢在早期就引入它的原因之一。
4.1 逻辑删除、乐观锁与多数据源
Plus 提供的能力 Flex 基本覆盖。逻辑删除全局配置后,删除语句自动转 update,查询自动带条件,不用每张表写死。乐观锁用版本字段校验并发更新,更新失败返回影响行数为 0,业务自行决定重试还是报错。项目里文章状态流转用乐观锁防并发覆盖,执行日志清理用逻辑删除兜底,两个场景都没再手写 SQL。
多数据源的差异更直接。Plus 要多引一个 mybatis-plus-dynamic-datasource 才能支持多数据源;Flex 把它内建,支持 Spring 事务管理器统一管理,非 Spring 环境也能用。项目现阶段单库够用,但团队另一条产品线已经在用 Flex 管理读写分离,少引依赖、配置面小是实打实的收益。
五、什么情况不该选 MyBatis-Flex
Flex 的生态和社区比 Plus 年轻,遇到问题能搜到的资料少,这是选型时必须接受的代价。老项目在 Plus 上已经沉淀了大量 XML 和拦截器配置,迁移收益不高,没必要动。团队都是 Plus 上手习惯、没时间读 Flex 文档的新团队,也别为了性能换。新项目、多表查询密集、想把数据权限和加密做进框架层的场景,Flex 是更合适的起点。
常见问题(FAQ)
Q1:MyBatis-Flex 比 MyBatis-Plus 快多少?
官方基准约快 5 到 10 倍,单条与分页查询均在此区间。
Q2:MyBatis-Flex 支持多表查询吗?
支持,QueryWrapper 直接写 join、union,无需 XML 或手写 SQL。
Q3:老项目从 Plus 迁到 Flex 成本高吗?
Mapper 基础写法兼容,但拦截器、XML 需改造,老项目不建议迁移。