一、MyBatis-Plus 基础认知
1. 什么是 MyBatis-Plus?
MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,遵循 "只做增强不做改变" 的核心原则,在不侵入原有 MyBatis 逻辑的前提下,通过封装通用能力简化开发流程。
2. 核心优势
表格
| 优势点 | 具体说明 |
|---|---|
| 无侵入 | 引入后无需修改现有 MyBatis 代码,完全兼容 MyBatis 所有功能 |
| 高效 CRUD | 内置 BaseMapper/IService,单表增删改查无需手写 SQL |
| 灵活查询 | Lambda 条件构造器避免字段硬编码错误,查询条件拼接更安全 |
| 便捷功能 | 内置主键生成(雪花算法)、分页、逻辑删除、乐观锁等常用功能 |
| 提效工具 | 代码生成器一键生成 Mapper/Model/Service/Controller 全套代码 |
3. 支持的数据库
MP 兼容所有支持 MyBatis 的标准 SQL 数据库,覆盖:
- 主流数据库:MySQL、Oracle、DB2、PostgreSQL、SQLServer、SQLite 等;
- 国产数据库:达梦、人大金仓、神通、瀚高、高斯等;
- 核心前提:数据库需遵循标准 SQL 语法,且有适配 MyBatis 的底层驱动。
4. 与 MyBatis 的核心区别
表格
| 特性 | MyBatis | MyBatis-Plus |
|---|---|---|
| 单表 CRUD | 手动编写 SQL/XML | 内置 BaseMapper,无需手写 |
| 条件查询 | 手动拼接 SQL | Lambda 条件构造器(Wrapper),更安全 |
| 分页 | 需手动配置插件 + 编写分页 SQL | 内置分页插件,Page 对象一键分页 |
| 主键生成 | 手动处理 | 多策略(自增、雪花算法、UUID 等) |
| 逻辑删除 / 乐观锁 | 手动实现 | 注解 + 配置即可开箱即用 |
二、核心功能与实现
1. 主键生成策略
MP 通过 @TableId 注解指定主键策略,适配不同业务场景:
| 策略类型 | 核心逻辑 | 适用场景 |
|---|---|---|
| AUTO | 数据库自增主键 | 单库、主键自增的简单场景 |
| INPUT | 手动输入主键值 | 需自定义主键(如业务编码) |
| ASSIGN_ID | 雪花算法生成分布式唯一 ID | 分布式系统、主键为 Long/String 类型 |
| ASSIGN_UUID | 生成 32 位无横线 UUID | 字符串主键、需全局唯一的场景 |
| NONE | 未指定,跟随全局配置 | 继承项目全局主键策略 |
扩展:雪花算法 = 41 位时间戳 + 10 位机器 ID + 12 位序列号,保证分布式 ID 唯一且趋势递增。
2. 分页功能实现
MP 分页需先配置拦截器,再通过 Page 对象实现,步骤如下:
步骤 1:配置分页拦截器(3.4.0+ 版本)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 指定数据库类型,避免分页语法兼容问题
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
步骤 2:代码中使用 Page 对象分页
@Test
public void testPage() {
// 参数:页码(从1开始)、每页条数
Page<User> page = new Page<>(2, 3);
// 分页查询(第二个参数为查询条件,null表示无条件)
Page<User> resultPage = userMapper.selectPage(page, null);
// 获取分页结果
List<User> dataList = resultPage.getRecords(); // 当前页数据
long total = resultPage.getTotal(); // 总条数
long pages = resultPage.getPages(); // 总页数
boolean hasNext = resultPage.hasNext(); // 是否有下一页
}
注意 :多表联查分页需使用 selectMapsPage,或手动拼接 COUNT SQL。
3. 逻辑删除
定义
逻辑删除是通过字段标记数据删除状态(而非物理删除),MP 自动将 DELETE 操作转为 UPDATE,查询时自动过滤已删除数据。
实现步骤
-
数据库:添加逻辑删除字段(如
deleted INT DEFAULT 0); -
实体类:字段添加
@TableLogic注解java
运行
public class User { @TableLogic // 标记逻辑删除字段 private Integer deleted; } -
配置文件:指定删除 / 未删除值 yaml
mybatis-plus: global-config: db-config: logic-delete-value: 1 # 已删除 logic-not-delete-value: 0 # 未删除
实现原理
- 删除操作:
DELETE FROM user WHERE id=1→ 自动转为UPDATE user SET deleted=1 WHERE id=1 AND deleted=0; - 查询操作:自动追加
AND deleted=0条件,无需手动编写。
4. 乐观锁
核心思想
假设数据不会发生并发冲突,仅在更新时检查版本号,版本一致则更新(版本号 +1),否则更新失败。
实现步骤
-
数据库:添加版本号字段(如
version INT DEFAULT 0); -
实体类:字段添加
@Version注解public class Product { @Version // 标记乐观锁版本字段 private Integer version; } -
配置乐观锁拦截器
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 乐观锁拦截器 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 分页拦截器(可选) interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
实现原理
更新 SQL 自动拼接版本条件:UPDATE product SET price=1500, version=version+1 WHERE id=1 AND version=0。
5. 条件构造器(Wrapper)
Wrapper 是 MP 条件构造核心,支持各类查询条件拼接,常用实现类及用法如下:
| 实现类 | 特点 |
|---|---|
| QueryWrapper | 普通条件构造(字段通过字符串指定) |
| LambdaQueryWrapper | Lambda 风格(字段方法引用,推荐,避免字段硬编码) |
| UpdateWrapper | 专用于更新操作的条件构造 |
常用 API 示例:
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "李四") // 等值查询
.ge(User::getAge, 18) // 大于等于
.like(User::getEmail, "test") // 模糊查询
.orderByDesc(User::getId); // 降序排序
List<User> list = userMapper.selectList(wrapper);
6. 自定义 SQL
MP 完全兼容 MyBatis 自定义 SQL 能力,实现步骤:
-
配置 XML 路径
mybatis-plus: mapper-locations: classpath:mapper/*.xml # XML文件路径 type-aliases-package: com.mp.entity # 实体别名包 -
Mapper 接口定义自定义方法
@Mapper public interface UserMapper extends BaseMapper<User> { // 自定义方法 List<User> selectByAge(Integer age); } -
编写 XML 映射文件(UserMapper.xml)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mp.mapper.UserMapper"> <select id="selectByAge" resultType="User"> SELECT * FROM user WHERE age = #{age} </select> </mapper> -
调用:
List<User> list = userMapper.selectByAge(18);
7. 日志配置
配置方式(控制台输出)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
日志作用
- 查看 MP 自动生成的 SQL,验证条件构造是否正确;
- 查看参数绑定结果,排查参数传递错误;
- 查看 SQL 执行时间,定位慢 SQL;
- 查看结果集,验证数据映射是否正确。
8. 常用注解
| 注解 | 作用 | 使用位置 |
|---|---|---|
| @TableName | 指定实体对应的数据库表名 | 实体类上 |
| @TableId | 标记主键,指定主键生成策略 | 主键字段上 |
| @TableField | 映射非主键字段(名称、是否为数据库字段) | 非主键字段上 |
| @TableLogic | 标记逻辑删除字段 | 逻辑删除字段上 |
| @Version | 标记乐观锁版本号字段 | 版本号字段上 |
| @EnumValue | 标记枚举类映射数据库的字段 | 枚举类字段上 |
示例:
@TableName("sys_user") // 表名映射
public class User {
@TableId(type = IdType.ASSIGN_ID) // 雪花算法主键
private Long id;
@TableField("user_name") // 字段名映射(name → user_name)
private String name;
}
三、实战问题解决
1. 实体类与数据库表字段名不一致
解决方式按优先级排序:
-
推荐 :开启下划线转驼峰(MP 默认开启),自动将数据库下划线字段(
user_name)映射为实体驼峰字段(userName); -
注解映射:通过
@TableName指定表名,@TableField(value = "字段名")指定列名; -
全局表前缀配置: yaml
mybatis-plus: global-config: db-config: table-prefix: sys_ # 所有表统一前缀(如 sys_user)
2. 分页插件查询总条数为 0 的排查步骤
- 检查分页拦截器:确认
MybatisPlusInterceptor已注入,且指定了正确的数据库类型; - 检查分页对象:必须使用 MP 的
Page对象,而非自定义分页类; - 检查条件构造器:确认 Wrapper 条件未过滤所有数据(如误加
eq("id", 0)); - 检查数据库权限:确认用户有查询
COUNT(*)的权限; - 多表联查场景:
selectPage仅支持单表,多表需用selectMapsPage或手动拼接 COUNT SQL。
3. 逻辑删除 vs 物理删除
表格
| 类型 | 操作方式 | 数据恢复 | 适用场景 |
|---|---|---|---|
| 物理删除 | DELETE 语句直接删除数据 | 不可恢复 | 测试数据、临时数据、无价值数据 |
| 逻辑删除 | UPDATE 语句标记删除状态 | 可恢复 | 用户、订单、商品等核心业务数据 |
4. 乐观锁 vs 悲观锁
表格
| 特性 | 乐观锁 | 悲观锁 |
|---|---|---|
| 核心思想 | 假设无并发冲突,更新时校验版本号 | 假设必有冲突,操作前加锁 |
| 实现方式 | MP 注解 + 版本号 | 数据库锁(如 SELECT ... FOR UPDATE) |
| 性能 | 无锁,性能高 | 加锁,性能较低 |
| 适用场景 | 读多写少(如商品库存) | 写多读少(如金融交易) |
四、核心要点总结
- MP 是 MyBatis 的增强工具,核心优势是 "无侵入 + 高效 CRUD + 丰富功能",完全兼容 MyBatis 原有特性;
- 核心功能落地:分页需配置拦截器 + Page 对象、逻辑删除靠注解 + 状态标记、乐观锁依赖版本号校验、条件构造优先用 Lambda 风格;
- 实战关键:字段映射优先用下划线转驼峰,核心数据用逻辑删除,分页总条数为 0 需排查拦截器 / 分页对象 / 多表联查。