MyBatis-Plus 核心功能?

2025年 阅读约 12 分钟 面试指南 · MyBatis

深入解析MyBatis-Plus核心功能:BaseMapper通用CRUD、条件构造器Wrapper体系、分页插件、代码生成器、自动填充、逻辑删除、乐观锁、与MyBatis的关系,附面试模拟问答。

一句话总结

MyBatis-Plus 是 MyBatis 的增强工具,只做增强不做改变。核心功能:BaseMapper(通用 CRUD,继承即用)、Wrapper 条件构造器(LambdaQueryWrapper 类型安全)、分页插件(PaginationInnerInterceptor)、代码生成器(AutoGenerator,生成 Entity/Mapper/Service/Controller)、自动填充(@TableField fill)、逻辑删除(@TableLogic)、乐观锁(@Version)。本质是 MyBatis 插件 + 代码生成 + 注解增强的组合。

初级理解

MyBatis-Plus vs MyBatis

对比维度MyBatisMyBatis-Plus
CRUD 操作需手写 SQLBaseMapper 内置 CRUD
条件查询手写动态 SQLWrapper 条件构造器
分页需引入 PageHelper内置分页插件
代码生成需第三方工具内置 AutoGenerator
关系基础框架增强工具(依赖 MyBatis)

BaseMapper 通用 CRUD

# 定义 Mapper 接口,继承 BaseMapper public interface UserMapper extends BaseMapper<User> { // 无需写任何方法,自动拥有以下能力 } # BaseMapper 提供的方法(部分): // 插入 int insert(T entity); // 删除 int deleteById(Serializable id); int deleteByMap(Map<String, Object> columnMap); int delete(Wrapper<T> wrapper); // 更新 int updateById(T entity); int update(T entity, Wrapper<T> updateWrapper); // 查询 T selectById(Serializable id); List<T> selectBatchIds(Collection<? extends Serializable> idList); List<T> selectByMap(Map<String, Object> columnMap); List<T> selectList(Wrapper<T> queryWrapper); Page<T> selectPage(Page<T> page, Wrapper<T> queryWrapper); # 使用示例: User user = userMapper.selectById(1L); List<User> users = userMapper.selectList(null); // 查全部 userMapper.insert(new User("张三", 25));

中级深入

Wrapper 条件构造器

# Wrapper 继承体系 Wrapper<T> (抽象类) ├── AbstractWrapper<T, R, Children> │ ├── QueryWrapper<T> # 字符串方式 │ ├── UpdateWrapper<T> # 更新条件 │ └── AbstractLambdaWrapper<T> │ ├── LambdaQueryWrapper<T> # Lambda 方式(推荐) │ └── LambdaUpdateWrapper<T> # Lambda 更新 # QueryWrapper(字符串方式,字段名硬编码) QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "张三") // name = '张三' .gt("age", 18) // age > 18 .like("email", "@qq.com") // email LIKE '%@qq.com%' .orderByDesc("create_time"); # LambdaQueryWrapper(推荐,类型安全) LambdaQueryWrapper<User> lambda = new LambdaQueryWrapper<>(); lambda.eq(User::getName, "张三") .gt(User::getAge, 18) .like(User::getEmail, "@qq.com") .orderByDesc(User::getCreateTime); # 常用条件方法: eq / ne # 等于 / 不等于 gt / ge / lt / le # 大于 / 大于等于 / 小于 / 小于等于 like / notLike / likeLeft / likeRight # 模糊查询 in / notIn # IN 查询 isNull / isNotNull # 空值判断 between / notBetween # 区间查询 orderByAsc / orderByDesc # 排序 groupBy / having # 分组 or / and # 逻辑拼接(默认 AND)

分页插件

# 1. 配置分页插件 @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor( new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } # 2. 使用分页 Page<User> page = new Page<>(1, 10); // 第1页,每页10条 Page<User> result = userMapper.selectPage(page, null); System.out.println("总记录数: " + result.getTotal()); System.out.println("总页数: " + result.getPages()); System.out.println("当前页数据: " + result.getRecords()); # 3. 分页 + 条件查询 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.gt(User::getAge, 18); Page<User> result = userMapper.selectPage( new Page<>(1, 10), wrapper); # 原理:PaginationInnerInterceptor 拦截 Executor.query() # 1. 先执行 COUNT 查询 # 2. 再执行分页查询(追加 LIMIT)

高级进阶

自动填充

# 1. 实体类标注 @TableField @Data public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; } # 2. 实现 MetaObjectHandler @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } } # FieldFill 枚举: # INSERT → 插入时填充 # UPDATE → 更新时填充 # INSERT_UPDATE → 插入和更新时都填充

逻辑删除

# 1. 全局配置 mybatis-plus: global-config: db-config: logic-delete-field: deleted # 逻辑删除字段 logic-delete-value: 1 # 已删除值 logic-not-delete-value: 0 # 未删除值 # 2. 实体类标注 @TableLogic @Data public class User { private Long id; @TableLogic private Integer deleted; // 0=未删除, 1=已删除 } # 效果: # userMapper.deleteById(1L); # → UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0 # userMapper.selectById(1L); # → SELECT * FROM user WHERE id = 1 AND deleted = 0 # 注意:逻辑删除后,所有查询自动拼接 deleted = 0 # 如需查询已删除数据,需自定义 SQL

乐观锁

# 1. 配置乐观锁插件 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } # 2. 实体类标注 @Version @Data public class User { private Long id; private String name; @Version private Integer version; // 版本号 } # 效果: # 更新时自动拼接 version 条件 # UPDATE user SET name = '李四', version = version + 1 # WHERE id = 1 AND version = 3 # 如果 version 不匹配(被其他线程更新过),更新失败 # 返回 affected rows = 0,需重试

实战场景

# 场景1:复杂条件查询 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.nested(w -> w.eq(User::getStatus, 1) .or() .eq(User::getStatus, 2)) .like(StringUtils.hasText(name), User::getName, name) .gt(age != null, User::getAge, age) .orderByDesc(User::getCreateTime); # 场景2:只更新部分字段 LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>(); wrapper.set(User::getName, "新名字") .set(User::getAge, 30) .eq(User::getId, 1L); userMapper.update(null, wrapper); # → UPDATE user SET name = '新名字', age = 30 WHERE id = 1 # 场景3:代码生成器 FastAutoGenerator.create( "jdbc:mysql://localhost:3306/test", "root", "123456") .globalConfig(builder -> builder .author("作者").outputDir("D:/code")) .packageConfig(builder -> builder .parent("com.example")) .strategyConfig(builder -> builder .addInclude("user", "order")) .execute();

面试模拟

面试官:MyBatis-Plus 和 MyBatis 的关系?解决了什么问题?

你:MyBatis-Plus 是 MyBatis 的增强工具,只做增强不做改变,引入它不会影响原有 MyBatis 功能。主要解决:1)BaseMapper 消除简单 CRUD 的重复 SQL;2)Wrapper 条件构造器替代手写动态 SQL;3)内置分页插件替代 PageHelper;4)代码生成器自动生成 Entity/Mapper/Service/Controller;5)自动填充、逻辑删除、乐观锁等开箱即用功能。

面试官:LambdaQueryWrapper 和 QueryWrapper 有什么区别?

你:QueryWrapper 用字符串指定字段名(如 eq("name", "张三")),字段名硬编码,重构时容易遗漏。LambdaQueryWrapper 用 Lambda 表达式(如 eq(User::getName, "张三")),编译期检查字段名,IDE 可自动重构,类型安全,推荐使用。