3215 字
约 10 分钟
13
MyBatis Plus
2026-09-19
无标签
第15章 MyBatis Plus
学习 MyBatis Plus 增强工具的使用和配置
学习进度:60% 代码演示按钮 💻 查看完整代码 - 在线IDE体验
🎯 学习目标
📚
理解核心概念
掌握本章的核心概念和基本原理
🔧
掌握实践技能
学会相关的配置方法和实践技巧
💡
了解应用场景
明确在实际项目中的应用场景和最佳实践
1. MyBatis Plus概述
1.1 什么是MyBatis Plus
MyBatis Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。
1.2 核心特性
🚀 无侵入
只做增强不做改变,引入它不会对现有工程产生影响
💪 损耗小
启动即会自动注入基本CRUD,性能基本无损耗
🔧 强大的CRUD操作
内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作
🎯 支持Lambda形式调用
通过Lambda表达式,方便的编写各类查询条件,无需再担心字段写错
📄 支持主键自动生成
支持多达4种主键策略,可自由配置,完美解决主键问题
📊 支持分页插件
基于MyBatis物理分页,开发者无需关心具体操作
2. 快速入门
2.1 依赖配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
2.2 配置数据源
# application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
table-prefix: mp_
2.3 创建实体类
@Data
@TableName("mp_users")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
2.4 创建Mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承BaseMapper后,无需编写任何代码即可获得CRUD功能
}
3. 基本CRUD操作
3.1 插入操作
// 插入一条记录
User user = new User();
user.setName("张三");
user.setAge(25);
user.setEmail("[email protected]");
int result = userMapper.insert(user);
System.out.println("插入结果: " + result);
System.out.println("主键ID: " + user.getId());
3.2 删除操作
// 根据ID删除
int result = userMapper.deleteById(1L);
// 根据条件删除
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
map.put("age", 25);
int result = userMapper.deleteByMap(map);
// 批量删除
List<Long> ids = Arrays.asList(1L, 2L, 3L);
int result = userMapper.deleteBatchIds(ids);
3.3 更新操作
// 根据ID更新
User user = new User();
user.setId(1L);
user.setName("李四");
user.setAge(26);
int result = userMapper.updateById(user);
3.4 查询操作
// 根据ID查询
User user = userMapper.selectById(1L);
// 查询所有
List<User> users = userMapper.selectList(null);
// 根据条件查询
Map<String, Object> map = new HashMap<>();
map.put("name", "张三");
List<User> users = userMapper.selectByMap(map);
// 批量查询
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.selectBatchIds(ids);
// 查询总数
Integer count = userMapper.selectCount(null);
4. 条件构造器
4.1 QueryWrapper
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三") // name = '张三'
.ne("age", 25) // age != 25
.gt("age", 18) // age > 18
.ge("age", 18) // age >= 18
.lt("age", 60) // age < 60
.le("age", 60) // age <= 60
.between("age", 18, 60) // age BETWEEN 18 AND 60
.notBetween("age", 18, 25) // age NOT BETWEEN 18 AND 25
.like("name", "张") // name LIKE '%张%'
.notLike("name", "王") // name NOT LIKE '%王%'
.likeLeft("name", "三") // name LIKE '%三'
.likeRight("name", "张") // name LIKE '张%'
.isNull("email") // email IS NULL
.isNotNull("email") // email IS NOT NULL
.in("age", 18, 25, 30) // age IN (18, 25, 30)
.notIn("age", 40, 50) // age NOT IN (40, 50)
.orderByAsc("age") // ORDER BY age ASC
.orderByDesc("create_time"); // ORDER BY create_time DESC
List<User> users = userMapper.selectList(queryWrapper);
4.2 LambdaQueryWrapper
// Lambda表达式(类型安全,避免字段名写错)
LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
lambdaQuery.eq(User::getName, "张三")
.ge(User::getAge, 18)
.le(User::getAge, 60)
.isNotNull(User::getEmail)
.orderByDesc(User::getCreateTime);
List<User> users = userMapper.selectList(lambdaQuery);
4.3 UpdateWrapper
// 更新条件构造器
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "张三")
.set("age", 26)
.set("email", "[email protected]");
int result = userMapper.update(null, updateWrapper);
// Lambda更新
LambdaUpdateWrapper<User> lambdaUpdate = new LambdaUpdateWrapper<>();
lambdaUpdate.eq(User::getName, "张三")
.set(User::getAge, 26)
.set(User::getEmail, "[email protected]");
int result = userMapper.update(null, lambdaUpdate);
4.4 复杂条件组合
// 复杂条件:(age > 18 AND age < 60) OR (name LIKE '%管理员%')
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.and(wrapper -> wrapper.gt("age", 18).lt("age", 60))
.or(wrapper -> wrapper.like("name", "管理员"));
// 嵌套查询
queryWrapper.exists("SELECT 1 FROM orders WHERE orders.user_id = mp_users.id");
// 动态条件
String name = "张三";
Integer minAge = 18;
Integer maxAge = 60;
queryWrapper.eq(StringUtils.isNotBlank(name), "name", name)
.ge(minAge != null, "age", minAge)
.le(maxAge != null, "age", maxAge);
5. 分页插件
5.1 配置分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
5.2 分页查询
// 创建分页对象:第1页,每页10条
Page<User> page = new Page<>(1, 10);
// 构造查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age", 18)
.orderByDesc("create_time");
// 执行分页查询
Page<User> userPage = userMapper.selectPage(page, queryWrapper);
// 获取分页信息
System.out.println("当前页: " + userPage.getCurrent());
System.out.println("每页大小: " + userPage.getSize());
System.out.println("总记录数: " + userPage.getTotal());
System.out.println("总页数: " + userPage.getPages());
System.out.println("是否有上一页: " + userPage.hasPrevious());
System.out.println("是否有下一页: " + userPage.hasNext());
System.out.println("当前页数据: " + userPage.getRecords());
5.3 自定义分页查询
// Mapper接口中定义
@Select("SELECT * FROM mp_users WHERE age BETWEEN #{minAge} AND #{maxAge}")
Page<User> selectUsersByAgeRange(Page<User> page,
@Param("minAge") Integer minAge,
@Param("maxAge") Integer maxAge);
// 使用
Page<User> page = new Page<>(1, 10);
Page<User> result = userMapper.selectUsersByAgeRange(page, 18, 60);
6. 乐观锁
6.1 配置乐观锁插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
6.2 实体类配置
@Data
public class User {
private Long id;
private String name;
private Integer age;
@Version // 乐观锁版本号字段
private Integer version;
}
6.3 乐观锁使用
// 查询用户
User user = userMapper.selectById(1L);
System.out.println("原始版本号: " + user.getVersion());
// 修改用户信息
user.setAge(user.getAge() + 1);
// 更新时会自动检查版本号
int result = userMapper.updateById(user);
if (result == 1) {
System.out.println("更新成功,新版本号: " + user.getVersion());
} else {
System.out.println("更新失败,可能存在并发冲突");
}
6.4 并发冲突演示
// 模拟并发更新
User user1 = userMapper.selectById(1L);
User user2 = userMapper.selectById(1L);
// 用户1先更新
user1.setAge(26);
int result1 = userMapper.updateById(user1); // 成功,version变为2
// 用户2后更新(此时version仍为1)
user2.setAge(27);
int result2 = userMapper.updateById(user2); // 失败,返回0
System.out.println("用户1更新结果: " + result1); // 1
System.out.println("用户2更新结果: " + result2); // 0
7. 逻辑删除
7.1 全局配置
# application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
7.2 实体类配置
@Data
public class User {
private Long id;
private String name;
private Integer age;
@TableLogic // 逻辑删除字段
private Integer deleted;
}
7.3 逻辑删除使用
// 删除操作(实际执行UPDATE语句)
int result = userMapper.deleteById(1L);
// 实际执行:UPDATE mp_users SET deleted=1 WHERE id=1 AND deleted=0
// 查询操作(自动过滤已删除数据)
List<User> users = userMapper.selectList(null);
// 实际执行:SELECT * FROM mp_users WHERE deleted=0
// 如需查询包含已删除的数据,需要自定义SQL
@Select("SELECT * FROM mp_users")
List<User> selectAllIncludeDeleted();
8. 自动填充
8.1 实体类配置
@Data
public class User {
private Long id;
private String name;
private Integer age;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
8.2 配置填充处理器
@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());
}
}
9. 服务层封装
9.1 IService接口
public interface UserService extends IService<User> {
// 继承IService后获得大量便捷方法
// 自定义业务方法
Page<User> getUsersByPage(int current, int size);
List<User> getUsersByAgeRange(Integer minAge, Integer maxAge);
}
9.2 ServiceImpl实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public Page<User> getUsersByPage(int current, int size) {
Page<User> page = new Page<>(current, size);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("create_time");
return this.page(page, queryWrapper);
}
@Override
public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.between("age", minAge, maxAge);
return this.list(queryWrapper);
}
}
9.3 IService常用方法
| 方法名 | 功能描述 | 示例 |
|---|---|---|
| save(entity) | 插入一条记录 | userService.save(user) |
| saveBatch(entityList) | 批量插入 | userService.saveBatch(users) |
| saveOrUpdate(entity) | 插入或更新 | userService.saveOrUpdate(user) |
| removeById(id) | 根据ID删除 | userService.removeById(1L) |
| removeBatchByIds(idList) | 批量删除 | userService.removeBatchByIds(ids) |
| updateById(entity) | 根据ID更新 | userService.updateById(user) |
| getById(id) | 根据ID查询 | userService.getById(1L) |
| list() | 查询所有 | userService.list() |
| page(page) | 分页查询 | userService.page(page) |
| count() | 查询总数 | userService.count() |
10. 代码生成器
10.1 添加依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
10.2 生成器配置
public class CodeGenerator {
public static void main(String[] args) {
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(
"jdbc:mysql://localhost:3306/mybatis_plus",
"root",
"123456"
).build();
// 全局配置
GlobalConfig globalConfig = new GlobalConfig.Builder()
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.author("MyBatis Plus")
.enableSwagger()
.build();
// 包配置
PackageConfig packageConfig = new PackageConfig.Builder()
.parent("com.mybatis.plus")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.build();
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig.Builder()
.addInclude("mp_users") // 指定表名
.entityBuilder()
.enableLombok()
.enableTableFieldAnnotation()
.logicDeleteColumnName("deleted")
.versionColumnName("version")
.addTableFills(new Column("create_time", FieldFill.INSERT))
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
.controllerBuilder()
.enableRestStyle()
.build();
// 执行生成
AutoGenerator generator = new AutoGenerator(dataSourceConfig);
generator.global(globalConfig)
.packageInfo(packageConfig)
.strategy(strategyConfig)
.execute();
}
}
11. 性能优化
11.1 SQL性能分析
# 开启SQL日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
11.2 分页优化
🔍 合理使用索引
- 为常用查询字段添加索引
- 避免在索引字段上使用函数
- 合理设计复合索引
📊 分页参数控制
- 限制每页最大记录数
- 避免查询过大的页码
- 使用游标分页处理大数据量
11.3 批量操作优化
// 批量插入优化
@Transactional
public boolean batchInsert(List<User> users) {
// 分批处理,避免一次性插入过多数据
int batchSize = 1000;
for (int i = 0; i < users.size(); i += batchSize) {
int end = Math.min(i + batchSize, users.size());
List<User> batch = users.subList(i, end);
userService.saveBatch(batch);
}
return true;
}
12. MyBatis vs MyBatis Plus对比
| 功能 | MyBatis | MyBatis Plus |
|---|---|---|
| 基本CRUD | 需要手写SQL和Mapper方法 | 继承BaseMapper即可获得 |
| 条件查询 | 需要手写动态SQL | 使用Wrapper构造器 |
| 分页查询 | 需要手动计算和编写 | 内置分页插件 |
| 乐观锁 | 需要手动实现 | @Version注解自动处理 |
| 逻辑删除 | 需要手动实现 | @TableLogic注解自动处理 |
| 自动填充 | 需要手动实现 | MetaObjectHandler自动处理 |
| 代码生成 | 需要第三方工具 | 内置代码生成器 |
| 学习成本 | 较高 | 较低 |
| 开发效率 | 一般 | 很高 |
13. 最佳实践
13.1 项目结构建议
- 实体类:使用@TableName、@TableId、@TableField等注解
- Mapper层:继承BaseMapper,添加自定义方法
- Service层:继承IService和ServiceImpl
- Controller层:使用RESTful风格API
13.2 配置建议
🔧 全局配置
- 统一主键策略
- 配置逻辑删除
- 设置表名前缀
- 开启驼峰命名转换
📊 性能配置
- 合理配置分页插件
- 启用乐观锁插件
- 配置SQL性能分析
- 设置合理的批量大小
13.3 开发建议
- 优先使用Lambda表达式:避免字段名写错
- 合理使用条件构造器:避免复杂的动态SQL
- 注意并发控制:合理使用乐观锁
- 规范命名:实体类、表名、字段名保持一致
14. 常见问题与解决方案
14.1 配置问题
问题:实体类与表名不匹配解决:使用@TableName注解指定表名,或配置全局表名前缀
问题:主键策略不生效解决:检查@TableId注解配置,确保数据库表主键设置正确
14.2 查询问题
问题:分页查询总数不准确解决:检查是否有逻辑删除字段影响,确保分页插件配置正确
问题:乐观锁更新失败解决:检查版本号字段类型,确保实体类有@Version注解
15. 实践练习
练习任务:
- 创建用户管理系统,使用MyBatis Plus实现CRUD操作
- 实现复杂条件查询(年龄范围、姓名模糊查询等)
- 添加分页查询功能
- 实现乐观锁并发控制
- 配置逻辑删除功能
- 使用代码生成器生成基础代码
16. 运行示例
💡 提示:本章节的完整代码示例已经准备就绪,你可以通过上方的"查看完整代码"按钮在在线IDE中运行和测试。
示例功能:
- ✅ 用户信息的增删改查操作
- ✅ 复杂条件查询演示
- ✅ 分页查询功能
- ✅ 乐观锁并发控制
- ✅ 逻辑删除演示
- ✅ 自动填充功能
- ✅ 批量操作优化
🎉
恭喜完成第15章学习!
你已经掌握了MyBatis Plus的核心功能和使用技巧
第16章
MyBatis 缓存机制
深入学习MyBatis的一级缓存和二级缓存机制,掌握缓存配置和优化策略。
🔥 中级⏱️ 45分钟开始学习 → ← 返回课程目录
评论
0 条
还没有评论,先写一条吧。