MyBatis-Plus面经
MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,凭借 “只做增强不做改变” 的特性大幅简化了数据库操作开发流程,是 Java 后端面试中的高频考点。本文整理了 MP 核心面试题,并结合实战场景给出详细解答,帮助大家吃透 MP 核心知识点。
一、基础概念类
1. 什么是 MyBatis-Plus?它的核心优势是什么?
答:MyBatis-Plus 是基于 MyBatis 的增强工具,在 MyBatis 基础上仅做增强不做改变,旨在简化开发、提高效率。核心优势:
- 无侵入:引入后不影响现有 MyBatis 工程逻辑;
- 强大的 CRUD 封装:内置通用 Mapper/Service,无需手动编写单表 CRUD SQL;
- 灵活的条件构造器:支持 Lambda 表达式编写查询条件,避免字段写错;
- 主键自动生成:支持 4 种以上主键策略(含雪花算法);
- 内置功能丰富:分页插件、性能分析插件、全局拦截插件、逻辑删除、乐观锁等;
- 代码生成器:快速生成 Mapper/Model/Service/Controller 层代码。
2. MyBatis-Plus 支持哪些数据库?
答:支持所有兼容 MyBatis 且遵循标准 SQL 的数据库,常见的有 MySQL、Oracle、DB2、PostgreSQL、SQLServer 等;也适配国产数据库(达梦、人大金仓、神通等)。
3. MyBatis-Plus 和 MyBatis 的区别是什么?
| 特性 | MyBatis | MyBatis-Plus |
|---|---|---|
| 单表 CRUD | 需手动编写 SQL/XML | 内置通用 Mapper,无需手动编写 |
| 条件查询 | 手动拼接 SQL | 条件构造器(Wrapper)+Lambda 表达式 |
| 分页 | 需手动配置分页插件 | 内置分页插件,支持多数据库 |
| 主键生成 | 手动处理 | 多种主键策略(自增、雪花算法、UUID 等) |
| 逻辑删除 / 乐观锁 | 需手动实现 | 注解 + 配置即可实现 |
二、核心功能类
4. MyBatis-Plus 的主键生成策略有哪些?分别适用于什么场景?
答:MP 通过@TableId(type = IdType.XXX)指定主键策略,核心策略及场景:
AUTO:数据库自增,适用于单库、主键自增的场景;INPUT:手动输入主键,适用于需自定义主键的场景;ASSIGN_ID:雪花算法生成分布式唯一 ID(Long/String 类型),适用于分布式系统;ASSIGN_UUID:生成 32 位 UUID,适用于主键为字符串且需全局唯一的场景;NONE:未设置,跟随全局配置。
扩展:雪花算法核心逻辑 ——41 位毫秒数 + 10 位机器 ID(5 位数据中心 + 5 位机器)+12 位毫秒内流水号,保证分布式环境下 ID 唯一且趋势递增。
5. 如何实现 MyBatis-Plus 的分页功能?
答:实现步骤:
- 配置分页拦截器(3.4.0 + 版本):
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 指定数据库类型(如MySQL) interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } - 代码中使用 Page 对象分页:
@Test public void testPage() { // 第2页,每页3条数据 Page<User> page = new Page<>(2, 3); // 分页查询(条件为null表示无过滤) Page<User> userPage = userMapper.selectPage(page, null); // 获取分页结果 List<User> records = userPage.getRecords(); // 分页数据 long total = userPage.getTotal(); // 总条数 long pages = userPage.getPages(); // 总页数 }
6. 什么是逻辑删除?MyBatis-Plus 如何实现逻辑删除?
答:逻辑删除:并非物理删除数据库数据,而是通过字段标记数据状态(如deleted=0未删除、deleted=1已删除),避免数据丢失。MP 实现逻辑删除步骤:
- 数据库表添加逻辑删除字段(如
deleted INT DEFAULT 0); - 实体类字段添加
@TableLogic注解:@TableLogic private Integer deleted; - 配置文件指定逻辑删除值:
mybatis-plus: global-config: db-config: logic-delete-value: 1 # 已删除值 logic-not-delete-value: 0 # 未删除值
原理:执行delete操作时,MP 自动转换为update(如DELETE FROM user WHERE id=1 → UPDATE user SET deleted=1 WHERE id=1 AND deleted=0);查询时自动追加deleted=0条件。
7. MyBatis-Plus 的乐观锁实现原理是什么?如何配置?
答:乐观锁核心思想:假设数据不会被并发修改,仅在更新时检查版本号,若版本号一致则更新,否则更新失败(解决并发修改冲突)。MP 实现乐观锁步骤:
- 数据库表添加版本号字段(如
version INT DEFAULT 0); - 实体类字段添加
@Version注解:@Version private Integer version; - 配置乐观锁拦截器:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 乐观锁拦截器 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 分页拦截器(若需分页可同时配置) interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
原理:更新时自动拼接版本号条件,且版本号自增(如UPDATE product SET price=1500, version=version+1 WHERE id=1 AND version=0)。
8. MyBatis-Plus 的条件构造器(Wrapper)有哪些常用用法?
答:Wrapper 是 MP 的条件构造器核心,常用实现类:QueryWrapper(查询)、UpdateWrapper(更新)、LambdaQueryWrapper(Lambda 风格)。常用 API 及场景:
// 1. 基础条件:姓名非空、邮箱非空、年龄≥12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age", 12);
List<User> list = userMapper.selectList(wrapper);
// 2. 等值查询:姓名=李四
wrapper.eq("name", "李四");
User user = userMapper.selectOne(wrapper);
// 3. 范围查询:年龄20~30
wrapper.between("age", 20, 30);
Integer count = userMapper.selectCount(wrapper);
// 4. 模糊查询:姓名不含"e"、邮箱以"t"开头
wrapper.notLike("name", "e")
.likeRight("email", "t");
// 5. 排序+子查询:id倒序、id≥2
wrapper.orderByDesc("id")
.inSql("id", "select id from user where id>=2");
// Lambda风格(避免字段写错)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(User::getName, "李四")
.ge(User::getAge, 18);
9. MyBatis-Plus 如何实现自定义 SQL?
答:MP 支持保留 MyBatis 的自定义 SQL 能力,步骤:
- 配置文件指定 Mapper 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
<?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" parameterType="int"> SELECT * FROM user WHERE age = #{age} </select> </mapper> - 调用自定义方法:
List<User> list = userMapper.selectByAge(18);
三、进阶应用类
10. MyBatis-Plus 的逻辑删除和物理删除的区别?实际项目中如何选择?
答:
- 物理删除:直接删除数据库数据,不可恢复,适用于无保留价值的数据(如测试数据、临时数据);
- 逻辑删除:仅标记删除状态,数据仍保留在库中,适用于需保留数据痕迹的场景(如用户、订单、商品等核心数据)。
选型建议:核心业务数据(订单、用户、交易记录)用逻辑删除;非核心临时数据用物理删除。
11. MyBatis-Plus 的乐观锁和悲观锁的区别?分别适用于什么场景?
| 特性 | 乐观锁 | 悲观锁 |
|---|---|---|
| 核心思想 | 假设不会并发冲突,更新时校验 | 假设会并发冲突,操作前加锁 |
| 实现方式 | 版本号(MP 注解 + 拦截器) | 数据库锁(如 FOR UPDATE) |
| 性能 | 无锁,性能高 | 加锁,性能较低 |
| 适用场景 | 读多写少、并发冲突少 | 写多读少、并发冲突多 |
示例场景:
- 乐观锁:商品库存(读多写少,偶尔并发);
- 悲观锁:金融交易(写多,需严格保证数据一致性)。
12. MyBatis-Plus 如何配置日志?为什么要配置日志?
答:配置日志可查看 MP 自动生成的 SQL 语句,便于调试和优化,配置方式(以控制台输出为例):
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
日志输出内容包含:执行的 SQL、参数、执行时间、结果集,可快速定位 SQL 拼接或条件查询的问题。
四、注解类
13. MyBatis-Plus 常用注解有哪些?分别作用是什么?
| 注解 | 作用 | 使用位置 |
|---|---|---|
@TableName | 指定实体类对应的数据表名 | 实体类上 |
@TableId | 标记主键字段,指定主键策略 | 实体类主键字段上 |
@TableField | 标记非主键字段(字段名映射、是否为数据库字段等) | 实体类非主键字段上 |
@TableLogic | 标记逻辑删除字段 | 实体类逻辑删除字段上 |
@Version | 标记乐观锁版本号字段 | 实体类版本号字段上 |
@EnumValue | 标记枚举类映射数据库的字段 | 枚举类字段上 |
示例:
@TableName("sys_user") // 表名映射
public class User {
@TableId(type = IdType.ASSIGN_ID) // 雪花算法主键
private Long id;
@TableField(value = "user_name") // 字段名映射(实体name → 表user_name)
private String name;
@TableLogic // 逻辑删除
private Integer deleted;
@Version // 乐观锁
private Integer version;
}
五、实战场景类
14. 实际项目中,如何解决 MyBatis-Plus 实体类与数据库表字段名不一致的问题?
答:有 3 种解决方案:
- 注解映射:通过
@TableName指定表名,@TableField(value = "字段名")指定列名; - 全局配置字段前缀:若表字段统一带前缀(如
sys_),可配置全局前缀:mybatis-plus: global-config: db-config: table-prefix: sys_ # 表前缀 - 开启下划线转驼峰(默认开启):MP 自动将数据库下划线字段(如
user_name)映射为实体驼峰字段(userName)。
15. MyBatis-Plus 的分页插件为什么查询总条数为 0?如何排查?
答:常见原因及排查步骤:
- 未配置分页拦截器:检查
MybatisPlusInterceptor是否注入,且指定了正确的数据库类型; - 分页对象使用错误:需使用 MP 的
Page对象,而非自定义分页类; - 数据库权限问题:确认用户有查询
COUNT(*)的权限; - 条件构造器过滤了所有数据:检查 Wrapper 条件是否正确;
- 多表联查:自定义 SQL 分页需手动处理总条数(
selectPage仅支持单表,多表需用selectMapsPage或手动拼接 COUNT SQL)。
更多推荐
所有评论(0)