MyBatis-Plus 入门
1. 一句话简介
MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在保留 MyBatis 原有灵活性的基础上,只做增强不做改变,为单表 CRUD、分页、条件查询等高频操作提供开箱即用的能力。它通过 BaseMapper<T>、IService<T> / ServiceImpl<M, T> 让 Mapper 和 Service 层无需编写任何 SQL 即可完成增删改查,并内置分页插件、字段自动填充、逻辑删除、ActiveRecord 模式等特性。
本 demo 模块(demo-orm-mybatis-plus)演示了其核心能力:UserMapper extends BaseMapper<User> 一行代码获得全部单表 CRUD;UserService extends IService<User> 配合 ServiceImpl 直接获得 save、saveBatch、page、count 等批量与分页方法;CommonFieldHandler 实现 MetaObjectHandler 自动填充 createTime、lastUpdateTime;Role extends Model<Role> 让实体对象直接调用 insert()、selectById() 操作数据库(ActiveRecord 模式)。一个 mybatis-plus-boot-starter 依赖即可替代「mybatis + tk.mybatis + PageHelper」三件套。
2. 什么时候使用
- ✅ 适用场景
- 以单表 CRUD 为主的业务系统(用户、订单、商品等),希望少写甚至不写 SQL,快速交付。
- 需要统一的分页能力,且不想额外引入 PageHelper 等独立分页组件——MP 内置
PaginationInterceptor物理分页。 - 需要公共字段(创建时间、更新时间、操作人等)自动填充,避免每个 Mapper 重复赋值。
- 需要逻辑删除、乐观锁、主键策略(自增 / 雪花 ID / UUID)等通用能力,通过配置即可开启。
- 团队已熟悉 MyBatis,希望保留手写复杂 SQL 的能力,同时提升单表开发效率。
- 需要代码生成器快速生成 entity / mapper / service 骨架,降低样板代码量。
- ❌ 不适用 / 需谨慎
- 复杂多表关联、动态报表、深度优化的 SQL:MP 的通用方法只覆盖单表,复杂查询仍需手写 XML/注解 SQL,此时其优势有限。
- 对 SQL 完全透明、追求极致可控的团队:MP 自动生成的 SQL 会隐藏部分细节,排查问题时需理解其生成逻辑。
- 需要跨数据库方言高度定制或非主流数据库:MP 对 MySQL 支持最好,其他数据库需额外验证。
- 已有大量手写 MyBatis XML 的存量项目:引入 MP 收益有限,且需评估与现有 Mapper 的共存成本。
- 对框架侵入性敏感、希望 ORM 层尽量薄的项目:MP 的
IService/ServiceImpl封装较厚,可能不符合「轻量」诉求。
3. 常见业务场景
- 用户 / 账号管理:用户表包含用户名、密码、盐、邮箱、手机号、状态、登录时间等字段,是典型的单表 CRUD。本 demo 中
UserService.save()插入用户、getById()查询、updateById()修改、removeById()删除,配合@TableField(fill = INSERT_UPDATE)自动填充时间字段,无需手写任何 SQL。 - 批量数据导入 / 初始化:一次性写入大量记录时,
IService.saveBatch()内部分批执行,性能优于逐条插入。demo 的testSaveList()循环构建 10 个用户后一次saveBatch()完成批量入库,并自动回写主键 id。 - 列表分页与条件筛选:后台管理列表普遍需要「分页 + 排序 + 多条件筛选」。demo 中
Page<>(1, 5)配合userService.page()实现物理分页,QueryWrapper.like("name", "Save1").or().eq("phone_number", ...).orderByDesc("id")链式构建复杂查询条件,等价于一段手写 SQL,但更简洁且类型安全(Lambda 写法)。 - 公共字段自动填充:几乎所有业务表都有创建时间、更新时间。通过实现
MetaObjectHandler并在实体字段上加@TableField(fill = INSERT)/INSERT_UPDATE,插入和更新时自动写入当前时间,避免每个 Service 手动 set,减少遗漏。 - ActiveRecord 模式(轻量实体操作):对于角色、字典等简单实体,让实体继承
Model<T>后可直接new Role().setId(1L).selectById()、role.insert()、role.deleteById(),无需经过 Service 层,适合简单数据对象的快速操作(demo 的Role即演示此模式)。
4. 同类技术对比
| 对比维度 | 原生 MyBatis | tk.mybatis(通用 Mapper) | MyBatis-Plus | JPA / Spring Data JPA |
|---|---|---|---|---|
| 单表 CRUD | 需手写 XML/注解 SQL | 继承 Mapper<T> 自动生成 |
继承 BaseMapper<T> 自动生成 |
继承 JpaRepository 自动生成 |
| 分页 | 需集成 PageHelper | 需集成 PageHelper | 内置 PaginationInterceptor |
内置 Pageable 分页 |
| 条件构造 | 无,手写 SQL | Example 对象 | QueryWrapper / LambdaQueryWrapper |
Specification / QueryDSL |
| 复杂 SQL 能力 | 强,完全可控 | 中,需手写 | 中,需手写 XML | 弱,复杂查询较繁琐 |
| 公共字段自动填充 | 无 | 无 | MetaObjectHandler |
@PrePersist / @PreUpdate |
| 通用 Service 封装 | 无 | 无 | IService + ServiceImpl |
内置 Repository 层 |
| 学习成本 | 高 | 中 | 低 | 中(需理解 JPA 规范) |
| 与 MyBatis 生态兼容 | 原生 | 兼容 | 兼容(增强) | 不兼容(另一套体系) |
| 适用规模 | 中大型、SQL 复杂 | 中小型单表为主 | 中小型单表为主、快速开发 | 中小型、领域模型驱动 |
选型建议:若项目以单表 CRUD 为主、追求快速开发且团队熟悉 MyBatis,优先选 MyBatis-Plus——它同时提供通用 Mapper、通用 Service、内置分页和自动填充,学习成本最低。若项目存在大量复杂多表 SQL 且需要完全掌控 SQL,选 原生 MyBatis 更合适。若已在用 tk.mybatis 且不想迁移,可继续沿用,但功能上 MP 基本是其超集。若团队倾向领域驱动、实体关系建模,且能接受 JPA 规范,可选 Spring Data JPA;但复杂查询和 SQL 调优上 JPA 不如 MyBatis 系灵活。