MyBatis-Plus-从 CRUD 到高级特性

一、为什么我们需要 MyBatis-Plus?

在讲 MyBatis-Plus 之前,我们先看看原生 MyBatis 有哪些痛点:

  1. 大量重复代码:每个表都要写重复的 CRUD 代码,开发效率低
  2. XML 文件臃肿:简单的查询也要写 SQL,XML 文件越来越大,难以维护
  3. 分页功能繁琐:需要手动写分页 SQL,或者集成第三方插件
  4. 条件查询麻烦:动态条件查询需要写大量的 if 判断,代码可读性差
  5. 没有统一的字段处理:比如创建时间、更新时间这些公共字段,每次都要手动设置

而 MyBatis-Plus 就是为了解决这些痛点而生的。它是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MyBatis-Plus 的核心优势

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
  • 损耗小:启动即会自动注入基本 CRUD,性能基本无损耗
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,只需要少量配置就能实现单表的大部分 CRUD 操作
  • 强大的条件构造器:可以轻松构建各种复杂的查询条件,不需要写 SQL
  • 内置分页插件:基于 MyBatis 物理分页,配置简单,不需要写分页 SQL
  • 丰富的插件:支持逻辑删除、自动填充、乐观锁、多数据源等插件
  • 代码生成器:一键生成实体类、Mapper、Service、Controller 层代码

二、Spring Boot 集成 MyBatis-Plus

集成 MyBatis-Plus 非常简单,只需要三步就能完成。

第一步:引入依赖

xml

复制代码
<!-- MyBatis-Plus启动器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

第二步:配置数据库连接

application.yml中添加数据库连接配置:

yaml

复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
    username: root
    password: 123456

# MyBatis-Plus配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 开启下划线转驼峰
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL打印
  global-config:
    db-config:
      id-type: auto # 主键自增
      logic-delete-field: deleted # 全局逻辑删除字段名
      logic-delete-value: 1 # 逻辑已删除值
      logic-not-delete-value: 0 # 逻辑未删除值

第三步:添加 Mapper 扫描注解

在启动类上添加@MapperScan注解,扫描 Mapper 接口:

java

运行

复制代码
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class MybatisPlusDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusDemoApplication.class, args);
    }
}

第四步:编写实体类和 Mapper 接口

java

运行

复制代码
// 实体类
@Data
@TableName("user")
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;
    
    @TableLogic
    private Integer deleted;
}

// Mapper接口
public interface UserMapper extends BaseMapper<User> {
}

没错,就这么简单!现在你已经可以使用 UserMapper 进行所有的单表 CRUD 操作了,不需要写任何 XML 文件和 SQL 语句。

三、基本 CRUD 操作

MyBatis-Plus 的 BaseMapper 接口提供了 17 个基本的 CRUD 方法,覆盖了所有常见的单表操作。

1. 插入操作

java

运行

复制代码
// 插入一条记录
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
userMapper.insert(user);

2. 更新操作

java

运行

复制代码
// 根据ID更新
User user = new User();
user.setId(1L);
user.setAge(21);
userMapper.updateById(user);

// 根据条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "张三").set("email", "zhangsan_new@example.com");
userMapper.update(null, updateWrapper);

3. 删除操作

java

运行

复制代码
// 根据ID删除
userMapper.deleteById(1L);

// 根据ID批量删除
userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));

// 根据条件删除
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20);
userMapper.delete(queryWrapper);

4. 查询操作

java

运行

复制代码
// 根据ID查询
User user = userMapper.selectById(1L);

// 根据ID批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));

// 查询所有
List<User> allUsers = userMapper.selectList(null);

// 根据条件查询一条记录
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三");
User oneUser = userMapper.selectOne(queryWrapper);

// 根据条件查询列表
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).orderByDesc("create_time");
List<User> userList = userMapper.selectList(queryWrapper);

// 查询总记录数
Long count = userMapper.selectCount(null);

四、核心功能详解

1. 条件构造器(Wrapper)

条件构造器是 MyBatis-Plus 最强大的功能之一。它可以让你不用写 SQL,就能构建各种复杂的查询条件。

MyBatis-Plus 提供了两种常用的条件构造器:

  • QueryWrapper:用于查询、删除操作
  • UpdateWrapper:用于更新操作
常用的条件方法

java

运行

复制代码
QueryWrapper<User> wrapper = new QueryWrapper<>();

// 等于
wrapper.eq("name", "张三");
// 不等于
wrapper.ne("name", "张三");
// 大于
wrapper.gt("age", 18);
// 大于等于
wrapper.ge("age", 18);
// 小于
wrapper.lt("age", 30);
// 小于等于
wrapper.le("age", 30);
// 模糊查询
wrapper.like("name", "张");
// 左模糊
wrapper.likeLeft("name", "张");
// 右模糊
wrapper.likeRight("name", "张");
// 包含
wrapper.in("age", Arrays.asList(18, 20, 22));
// 不包含
wrapper.notIn("age", Arrays.asList(18, 20, 22));
// 范围查询
wrapper.between("age", 18, 30);
// 不为空
wrapper.isNotNull("email");
// 为空
wrapper.isNull("email");
// 排序
wrapper.orderByAsc("age");
wrapper.orderByDesc("create_time");
// 分组
wrapper.groupBy("age");
// 分页
wrapper.last("limit 10");
动态条件查询

条件构造器支持链式调用和动态条件,非常适合处理复杂的查询场景:

java

运行

复制代码
public List<User> searchUsers(String name, Integer minAge, Integer maxAge) {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    
    // 动态条件:如果参数不为空,才添加条件
    if (StringUtils.hasText(name)) {
        wrapper.like("name", name);
    }
    if (minAge != null) {
        wrapper.ge("age", minAge);
    }
    if (maxAge != null) {
        wrapper.le("age", maxAge);
    }
    
    wrapper.orderByDesc("create_time");
    return userMapper.selectList(wrapper);
}

2. 分页插件

MyBatis-Plus 内置了强大的分页插件,只需要简单配置就能使用。

第一步:配置分页插件

java

运行

复制代码
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
第二步:使用分页查询

java

运行

复制代码
// 分页查询:第1页,每页10条
Page<User> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);

Page<User> userPage = userMapper.selectPage(page, wrapper);

// 获取分页数据
List<User> records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总记录数
long pages = userPage.getPages(); // 总页数
long current = userPage.getCurrent(); // 当前页
long size = userPage.getSize(); // 每页条数

3. 逻辑删除

逻辑删除是指不是真正的从数据库中删除数据,而是通过一个字段标记数据是否被删除。MyBatis-Plus 内置了逻辑删除功能,只需要简单配置就能使用。

第一步:在数据库表中添加逻辑删除字段

sql

复制代码
ALTER TABLE user ADD COLUMN deleted INT DEFAULT 0 COMMENT '逻辑删除字段:0-未删除,1-已删除';
第二步:在实体类中添加注解

java

运行

复制代码
@Data
@TableName("user")
public class User {
    // 其他字段...
    
    @TableLogic
    private Integer deleted;
}

现在,所有的删除操作都会自动变成更新操作,更新deleted字段为 1;所有的查询操作都会自动过滤掉已删除的数据。

4. 自动填充

自动填充功能可以自动为创建时间、更新时间这些公共字段赋值,不需要每次手动设置。

第一步:在实体类中添加注解

java

运行

复制代码
@Data
@TableName("user")
public class User {
    // 其他字段...
    
    @TableField(fill = FieldFill.INSERT) // 插入时自动填充
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
    private LocalDateTime updateTime;
}
第二步:实现元对象处理器

java

运行

复制代码
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
    }
}

现在,当你插入或更新数据时,createTimeupdateTime字段会自动填充当前时间。

5. 代码生成器

MyBatis-Plus 的代码生成器可以一键生成实体类、Mapper、Service、Controller 层的代码,大大提高开发效率。

第一步:引入代码生成器依赖

xml

复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3.1</version>
</dependency>

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>
第二步:编写代码生成器

java

运行

复制代码
public class CodeGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus_demo", "root", "123456")
                .globalConfig(builder -> {
                    builder.author("your name") // 设置作者
                            .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
                            .disableOpenDir(); // 生成后不打开目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.example.demo") // 父包名
                            .entity("entity") // 实体类包名
                            .mapper("mapper") // Mapper包名
                            .service("service") // Service包名
                            .controller("controller"); // Controller包名
                })
                .strategyConfig(builder -> {
                    builder.addInclude("user", "order", "product") // 需要生成的表名
                            .entityBuilder()
                            .enableLombok() // 开启Lombok
                            .logicDeleteColumnName("deleted") // 逻辑删除字段名
                            .controllerBuilder()
                            .enableRestStyle(); // 开启RestController
                })
                .execute();
    }
}

运行这个 main 方法,就能一键生成所有层的代码,非常方便。

五、常见问题与解决方案

问题 1:分页插件不生效

常见原因

  • 没有配置分页插件
  • 分页插件配置错误
  • 使用了自定义的 SQL,没有走 MyBatis-Plus 的分页

解决方案

  • 确保正确配置了MybatisPlusInterceptorPaginationInnerInterceptor
  • 确保数据库类型配置正确
  • 自定义 SQL 需要使用Page作为参数,并且返回Page对象

问题 2:条件构造器的字段名写错

问题描述:条件构造器中使用了 Java 字段名,而不是数据库字段名,导致 SQL 错误。

解决方案

  • 条件构造器中使用的是数据库字段名,不是 Java 字段名
  • 如果开启了下划线转驼峰,数据库字段名是下划线格式,Java 字段名是驼峰格式

问题 3:逻辑删除后查询不到数据

问题描述:逻辑删除后,查询时没有自动过滤已删除的数据。

解决方案

  • 确保实体类上添加了@TableLogic注解
  • 确保全局配置中正确配置了逻辑删除字段
  • 自定义 SQL 需要手动添加逻辑删除条件

问题 4:自动填充不生效

常见原因

  • 没有实现MetaObjectHandler接口
  • 实现类没有添加@Component注解
  • 字段上没有添加@TableField(fill = ...)注解

解决方案

  • 检查以上三点,确保配置正确
  • 确保字段类型和填充值的类型一致

六、生产环境最佳实践

  1. 不要过度使用条件构造器:对于非常复杂的查询,建议直接写 XML SQL,可读性和可维护性更好
  2. 合理使用代码生成器:代码生成器可以生成基础代码,但业务逻辑还是需要自己写
  3. 开启 SQL 打印:开发环境开启 SQL 打印,方便调试和排查问题
  4. 使用分页插件:所有的分页查询都使用 MyBatis-Plus 的分页插件,不要自己写分页 SQL
  5. 使用逻辑删除:生产环境建议使用逻辑删除,不要物理删除数据
  6. 使用自动填充:创建时间、更新时间这些公共字段使用自动填充
  7. 避免使用 selectByIds 查询大量数据:如果 ID 数量很多,会导致 SQL 过长,性能下降
  8. 定期优化 SQL:使用慢查询日志,找出执行慢的 SQL 进行优化
  9. 不要在 Service 层直接使用 Mapper:建议在 Service 层封装业务逻辑,Controller 层调用 Service 层
  10. 统一异常处理:结合全局异常处理,处理 MyBatis-Plus 抛出的异常

总结

MyBatis-Plus 是一个非常优秀的 ORM 框架,它大大简化了 MyBatis 的开发,让我们从繁琐的 CRUD 代码中解放出来,专注于业务逻辑的实现。

回顾一下本文的核心内容:

  • MyBatis-Plus 是 MyBatis 的增强工具,解决了原生 MyBatis 的诸多痛点
  • 集成 MyBatis-Plus 非常简单,只需要三步就能完成
  • BaseMapper 提供了 17 个基本的 CRUD 方法,覆盖了所有常见的单表操作
  • 条件构造器可以轻松构建各种复杂的查询条件,不需要写 SQL
  • 内置分页插件、逻辑删除、自动填充、代码生成器等强大功能
  • 生产环境中要注意避免常见的坑,遵循最佳实践
相关推荐
就像风一样抓不住2 小时前
Java 手机号校验工具类
java
凤山老林2 小时前
26-Java this 关键字
java·开发语言
焦糖玛奇朵婷2 小时前
解锁扭蛋机小程序的五大优势
java·大数据·服务器·前端·小程序
SamDeepThinking2 小时前
别让一个超时的第三方http接口拖垮所有接口
java·后端·架构
YaBingSec3 小时前
玄机靶场:供应链安全-供应链应急-Part2 通关笔记
java·笔记·安全
Gerardisite3 小时前
企微机器人开发指南
java·python·机器人·自动化·企业微信
OtIo TALL3 小时前
Java进阶(ElasticSearch的安装与使用)
java·elasticsearch·jenkins
一 乐3 小时前
交通感知与车路协同系统|基于springboot + vue交通感知与车路协同系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·交通感知与车路协同系统
Java面试题总结3 小时前
FVG3 构建系统 MinGW 配置问题排查文档
java