3215 字
约 10 分钟
13
MyBatis Plus
无标签

第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. 实践练习

练习任务:

  1. 创建用户管理系统,使用MyBatis Plus实现CRUD操作
  2. 实现复杂条件查询(年龄范围、姓名模糊查询等)
  3. 添加分页查询功能
  4. 实现乐观锁并发控制
  5. 配置逻辑删除功能
  6. 使用代码生成器生成基础代码

16. 运行示例

💡 提示:本章节的完整代码示例已经准备就绪,你可以通过上方的"查看完整代码"按钮在在线IDE中运行和测试。

示例功能:

  • ✅ 用户信息的增删改查操作
  • ✅ 复杂条件查询演示
  • ✅ 分页查询功能
  • ✅ 乐观锁并发控制
  • ✅ 逻辑删除演示
  • ✅ 自动填充功能
  • ✅ 批量操作优化

🎉

恭喜完成第15章学习!

你已经掌握了MyBatis Plus的核心功能和使用技巧
第16章

MyBatis 缓存机制

深入学习MyBatis的一级缓存和二级缓存机制,掌握缓存配置和优化策略。
🔥 中级⏱️ 45分钟开始学习 → ← 返回课程目录

MyBatis Plus
https://clxhxhhr.top/posts/2857/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。