MyBatis-Plus
1. 是什么?
MyBatis-Plus(简称 MP)是一个 基于 MyBatis 的增强工具,在 MyBatis 的基础上只做增强,不做改变。它的目标是 简化开发、提高效率,让开发者“只写 SQL 的 20%,完成 80% 的 CRUD 工作”。
官方口号:为简化开发而生。
MyBatis-Plus 并不是一个独立框架,而是 MyBatis 的插件式扩展。它保留了 MyBatis 的所有特性(如手写 SQL、动态 SQL、结果映射等),同时提供了:
- 通用 CRUD 封装(无需写 Mapper XML)
- 条件构造器(Wrapper,链式编程构建 WHERE 条件)
- 分页插件
- 自动填充(如创建时间、更新时间)
- 乐观锁支持
- 逻辑删除
- 代码生成器
本质上,MyBatis-Plus = MyBatis + 自动化 CRUD + 实用工具集。
2. 没有之前?
在 MyBatis-Plus 出现之前(项目始于 2016 年),开发者使用 原生 MyBatis 时仍面临以下重复性工作:
| 问题 | 说明 |
|---|---|
| 大量重复的 CRUD XML | 即使是简单的增删改查,每个实体都要写 <select>, <insert> 等标签,内容高度相似。 |
| 手写条件查询繁琐 | 多条件查询需在 XML 中写大量 <if> 标签,或拼接字符串,易出错。 |
| 分页需手动处理 | 不同数据库分页语法不同(MySQL 用 LIMIT,Oracle 用 ROWNUM),需自行适配。 |
| 公共字段需手动赋值 | 如 create_time, update_time, create_by 等字段,每次插入/更新都要显式设置。 |
| 逻辑删除需改 SQL | 软删除(is_deleted = 1)需在每个查询中加 AND is_deleted = 0,容易遗漏。 |
| 样板代码多 | Service 层常写 xxxMapper.selectById(id) 这类重复调用。 |
原生 MyBatis 的典型痛点示例:
<!-- UserMapper.xml -->
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">AND name LIKE CONCAT('%', #{name}, '%')</if>
<if test="status != null">AND status = #{status}</if>
AND is_deleted = 0 <!-- 逻辑删除,每处都要加! -->
</where>
</select>每个表都要写类似逻辑,维护成本高。
3. 为什么使用?
MyBatis-Plus 的核心价值是:在保留 MyBatis 灵活性的前提下,消灭 90% 的样板代码。
- 无侵入:仅依赖 MyBatis,不修改其核心机制。
- 强大的 CRUD 封装:继承
BaseMapper<T>即可获得 17+ 个通用方法(如selectById,updateById,delete等)。 - 链式条件构造器(Wrapper):用 Java 代码构建 WHERE 条件,类型安全、可读性强。
- 自动分页:一行配置支持多数据库分页,无需写
LIMIT或ROWNUM。 - 自动填充:插入/更新时自动填充字段(如时间戳、操作人)。
- 逻辑删除:全局配置后,所有查询自动过滤已删除数据。
- 乐观锁插件:通过
@Version注解实现并发控制。 - 代码生成器:一键生成 Entity、Mapper、Service、Controller,极大提升开发速度。
- 与 Spring Boot 无缝集成:自动配置,开箱即用。
适用场景:快速开发后台管理系统、CRUD 密集型应用、需要兼顾效率与灵活性的项目。
4. 核心组件 / 概念
| 组件/概念 | 说明 |
|---|---|
| BaseMapper | 通用 Mapper 接口,继承后自动获得 CRUD 方法。 |
| IService / ServiceImpl<M, T> | 通用 Service 层封装,提供批量操作、分页查询等高级方法。 |
| QueryWrapper / UpdateWrapper | 条件构造器,用于构建 WHERE、SET 子句,支持链式调用。 |
| LambdaQueryWrapper | 基于 Lambda 表达式的 Wrapper,避免硬编码字段名(类型安全)。 |
| MyMetaObjectHandler | 自动填充处理器,用于 insertFill / updateFill。 |
| PaginationInterceptor(旧版) / MybatisPlusInterceptor(新版) | 分页插件,注册后 Page<T> 对象自动分页。 |
| @TableName, @TableId, @TableField | 实体类注解,用于配置表名、主键策略、字段映射、逻辑删除等。 |
| CodeGenerator | 代码生成器,根据数据库表自动生成全套 CRUD 代码。 |
5. 使用步骤
步骤 1:添加依赖(Spring Boot 项目)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>步骤 2:配置 application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL
global-config:
db-config:
id-type: auto # 主键自增
logic-delete-value: 1 # 逻辑删除值(已删除)
logic-not-delete-value: 0 # 未删除值步骤 3:定义实体类(Entity)
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data
@TableName("user") // 指定表名
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private String email;
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时填充
private LocalDateTime updateTime;
@TableLogic // 逻辑删除字段
private Integer deleted; // 0-未删除, 1-已删除
}步骤 4:定义 Mapper 接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 无需写任何方法!已继承 selectById, insert, updateById 等
}步骤 5:定义 Service(可选但推荐)
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserService extends ServiceImpl<UserMapper, User> implements IService<User> {
// 可扩展自定义方法
}步骤 6:配置自动填充 & 分页插件(Java Config)
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
// 自动填充处理器
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
// 分页插件(MyBatis-Plus 3.4.0+)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}6. 示例代码(完整 CRUD + 条件查询)
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 1. 新增
@PostMapping
public boolean save(@RequestBody User user) {
return userService.save(user); // 自动填充 createTime/updateTime
}
// 2. 删除(逻辑删除)
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Long id) {
return userService.removeById(id); // 实际执行 UPDATE SET deleted = 1
}
// 3. 修改
@PutMapping
public boolean update(@RequestBody User user) {
return userService.updateById(user); // 自动填充 updateTime
}
// 4. 查询 by ID
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
return userService.getById(id); // 自动忽略 deleted=1 的记录
}
// 5. 条件查询 + 分页
@GetMapping("/list")
public IPage<User> list(
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "1") int current,
@RequestParam(defaultValue = "10") int size) {
// 使用 LambdaQueryWrapper 构建条件(类型安全)
LambdaQueryWrapper<User> query = Wrappers.<User>lambdaQuery()
.like(StringUtils.isNotBlank(name), User::getName, name);
Page<User> page = new Page<>(current, size);
return userService.page(page, query);
}
}上述代码 无需任何 XML 文件,所有 CRUD 和条件查询均由 MyBatis-Plus 自动生成 SQL。
7. 局限性
尽管 MyBatis-Plus 极大提升了开发效率,但也存在一些限制:
| 局限 | 说明 |
|---|---|
| 仅适用于单表操作 | 复杂多表 JOIN 仍需手写 SQL(可通过 @Select 注解或 XML 补充)。 |
| 学习新 API | 需掌握 Wrapper、IService 等新概念,对纯 MyBatis 用户有学习成本。 |
| 过度封装风险 | 初学者可能不了解底层 SQL,导致性能问题(如未加索引的模糊查询)。 |
| 版本兼容性 | 不同版本间 API 可能变化较大(如分页插件从 PaginationInterceptor 改为 MybatisPlusInterceptor)。 |
| 不适合超复杂业务 | 若 80% 以上都是定制 SQL,MP 的优势会减弱。 |
| 调试需看日志 | 自动生成的 SQL 需开启日志才能查看,不如手写直观。 |
总结
| 维度 | 内容 |
|---|---|
| 是什么 | MyBatis 的增强工具,提供通用 CRUD、条件构造器、分页等 |
| 没有之前 | 原生 MyBatis 需手写大量重复 XML 和条件判断 |
| 为什么用 | 消灭样板代码、提升开发效率、保留 MyBatis 灵活性 |
| 核心组件 | BaseMapper, IService, Wrapper, 自动填充, 逻辑删除, 代码生成器 |
| 使用步骤 | 依赖 → 配置 → 实体(注解)→ Mapper(继承 BaseMapper)→ Service → 调用 |
| 局限性 | 多表查询支持弱、需学习新 API、复杂场景仍需手写 SQL |
推荐使用场景:
- 后台管理系统(Admin System)
- 快速原型开发(MVP)
- 单表 CRUD 为主的业务系统
❌ 慎用场景:
- 高度复杂的 OLAP 报表系统
- 多表深度关联、频繁使用存储过程的遗留系统
MyBatis-Plus 是当前国内 Java 开发中最流行的持久层解决方案之一,尤其适合追求 开发效率 + 灵活性 的团队。如果已经熟悉 MyBatis,上手 MP 几乎零成本;如果是新手,MP 也能快速上手写出专业级数据访问层。
关联网络
演化日志
- v0.1 (2026-02-02):补充关联网络、演化日志、附件参考、待办事项
待办事项
- OLAP 报表系统
- 存储过程
- 代码生成器使用、多数据源配置、与 Spring Security 集成