Mybatis-Plus:让CRUD快如闪电

一、引言

在 SpringBoot 开发中,Mybatis 一直是国内最流行的持久层框架。但它有个让人头疼的地方:每写一个简单的增删改查,都要去 XML 或注解里手写一遍 SQL,即使只是 select * from user where id = ? 这种毫无技术含量的语句。

Mybatis-Plus(简称 MP)就是为「干掉重复的简单 SQL」而生的。它在 Mybatis 的基础上只做增强、不做改变,能让我们用极少量的代码完成绝大多数 CRUD 操作。本文就从零带你上手 Mybatis-Plus。

二、Mybatis-Plus 是什么

Mybatis-Plus 是一个 Mybatis 的增强工具,由国人开发(作者「青苗」),目前已经是国内使用最广的 ORM 增强框架之一。核心思想:在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

它提供了以下核心能力:

  • 通用 BaseMapper:内置单表的增删改查,无需写任何 SQL;
  • 强大的条件构造器:QueryWrapper / LambdaQueryWrapper 链式拼条件;
  • 内置分页插件;
  • 代码生成器、逻辑删除、乐观锁、自动填充等开箱即用的功能。

三、相比原生 Mybatis 的优势

对比项 原生 Mybatis Mybatis-Plus
简单 CRUD 需手写 SQL 或注解 BaseMapper 一键搞定
分页 需引入 PageHelper 或手写 内置分页插件
条件拼接 动态 SQL 手写 <if> 等 条件构造器链式调用
字段名书写 字符串易写错 Lambda 方式类型安全
代码量 大 小

一句话总结:**原生 Mybatis 该有的能力它全保留,繁琐的简单 CRUD 它全替你做了。**​

四、快速上手:集成 SpringBoot

4.1 引入依赖

本文使用 SpringBoot 3.x,Mybatis-Plus 需使用 mybatis-plus-spring-boot3-starter:

xml

复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<!-- 简化实体类,非必须,但强烈推荐 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

注意:如果是 SpringBoot 2.x,请使用 mybatis-plus-boot-starter。

4.2 配置数据源

application.yml:

yaml

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

mybatis-plus:
  configuration:
    # 控制台打印 SQL,方便调试
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: auto   # 主键自增

4.3 准备数据表

sql

复制代码
CREATE DATABASE IF NOT EXISTS mp_demo DEFAULT CHARACTER SET utf8mb4;
USE mp_demo;

CREATE TABLE `user` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` VARCHAR(50) DEFAULT NULL COMMENT '姓名',
  `age` INT DEFAULT NULL COMMENT '年龄',
  `email` VARCHAR(100) DEFAULT NULL COMMENT '邮箱',
  `create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

4.4 实体类 User

java

复制代码
package com.example.mp.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;

@Data
@TableName("user")   // 指定对应表名(类名与表名不一致时必填)
public class User {

    @TableId(type = IdType.AUTO)  // 主键自增
    private Long id;

    private String name;

    private Integer age;

    private String email;

    private LocalDateTime createTime;
}

4.5 Mapper 接口

java

复制代码
package com.example.mp.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承 BaseMapper 后,单表 CRUD 已经全部内置,这里可以什么都不写
}

启动类记得加 @MapperScan(或每个 Mapper 上手动加 @Mapper,二选一):

java

复制代码
package com.example.mp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mp.mapper")
public class MpDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpDemoApplication.class, args);
    }
}

五、BaseMapper 通用 CRUD

继承 BaseMapper 后,我们就白拿了大量通用方法。看一个完整的 Service 示例:

java

复制代码
package com.example.mp.service;

import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    /** 增:插入一条记录 */
    public void insert() {
        User user = new User();
        user.setName("张三");
        user.setAge(20);
        user.setEmail("zhangsan@example.com");
        user.setCreateTime(LocalDateTime.now());
        userMapper.insert(user);
        // 插入成功后,自增主键会自动回填到 user.getId()
        System.out.println("插入成功,主键 = " + user.getId());
    }

    /** 删:按主键删 / 批量删 */
    public void delete() {
        userMapper.deleteById(1L);                          // 按主键删除
        userMapper.deleteBatchIds(Arrays.asList(2L, 3L));   // 按主键批量删除
    }

    /** 改:按主键更新(只更新非 null 字段) */
    public void update() {
        User user = new User();
        user.setId(1L);
        user.setAge(30);   // 只更新 age 字段,其余字段为 null 不会被更新
        userMapper.updateById(user);
    }

    /** 查:按主键查 / 批量查 / 查所有 */
    public void select() {
        User user = userMapper.selectById(1L);
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
        List<User> all = userMapper.selectList(null);  // 传 null 表示无条件,查全部
        long count = userMapper.selectCount(null);     // 查总数
    }
}

BaseMapper 还提供了 insertBatch、update、selectPage、selectOne 等数十个方法,覆盖了绝大多数单表操作场景。

六、条件构造器:QueryWrapper 与 LambdaQueryWrapper

当查询条件变复杂时,就需要条件构造器登场了。

java

复制代码
package com.example.mp.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class WrapperDemoService {

    @Autowired
    private UserMapper userMapper;

    public void queryWrapperDemo() {
        // 方式一:QueryWrapper ------ 字段名以字符串形式书写
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.eq("age", 20)                 // age = 20
          .like("name", "张")            // name LIKE '%张%'
          .between("age", 18, 30)        // age BETWEEN 18 AND 30
          .orderByDesc("id");            // ORDER BY id DESC
        List<User> list1 = userMapper.selectList(qw);

        // 方式二:LambdaQueryWrapper ------ 基于 Lambda,类型安全,字段名不会写错
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getAge, 20)
           .like(User::getName, "张")
           .between(User::getAge, 18, 30)
           .orderByDesc(User::getId);
        List<User> list2 = userMapper.selectList(lqw);
    }
}

常用条件方法速查:

方法 含义 示例
eq 等于 = eq("age", 20)
ne 不等于 != ne("age", 20)
gt / ge 大于 / 大于等于 gt("age", 18)
lt / le 小于 / 小于等于 le("age", 30)
like / likeLeft / likeRight 模糊匹配 like("name", "张")
between 区间 between("age", 18, 30)
in 集合 in("id", 1, 2, 3)
isNull / isNotNull 判空 isNull("email")
orderByAsc / orderByDesc 排序 orderByDesc("id")

推荐日常使用 LambdaQueryWrapper:一旦字段名重命名,编译器会直接报错,避免运行时才暴露的字符串拼写问题。

七、分页插件配置

Mybatis-Plus 的分页需要先注册一个插件,非常轻量。

配置类:

java

复制代码
package com.example.mp.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件:指定数据库类型为 MySQL
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

使用示例:

java

复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PageDemoService {

    @Autowired
    private UserMapper userMapper;

    public void pageDemo() {
        Page<User> page = new Page<>(1, 5);  // 第 1 页,每页 5 条
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.orderByDesc(User::getId);
        Page<User> result = userMapper.selectPage(page, lqw);

        System.out.println("总记录数:" + result.getTotal());
        System.out.println("总页数:" + result.getPages());
        System.out.println("当前页数据:" + result.getRecords());
    }
}

八、总结

Mybatis-Plus 的价值可以概括为一句话:它让「写 SQL」这件事从每天必做,变成了只有复杂场景才需要做的事。

本文我们完成了:

  1. 认识了 Mybatis-Plus 及它相对原生 Mybatis 的优势;
  2. 完成了 SpringBoot 集成(依赖、配置、实体、Mapper);
  3. 用 BaseMapper 实现了零 SQL 的通用 CRUD;
  4. 用 QueryWrapper / LambdaQueryWrapper 拼复杂条件;
  5. 配置并使用了内置分页插件。

掌握这些,日常单表开发基本可以告别手写 SQL 了。

相关推荐
smallyoung2 分钟前
告别N+1:Spring Boot 一键数据翻译框架体系
java·后端·架构
Bazingga2 小时前
Java16 record 完整拆解——语法、语义、限制、反射
java·后端
SL_staff3 小时前
规则联动:给工厂装上‘自动化神经反射弧’——JVS·物联网平台规则引擎实战教程
java·人工智能·物联网
骑着蜗牛撵大象3273 小时前
Linux 进程间通信之管道:从 pipe() 的内核语义到 FIFO 落地排错
android·java·linux·系统编程·fifo·管道·进程间通信
WJ.Polar4 小时前
Java封装、继承、多态
java·开发语言
ly76894 小时前
PostgreSQL 备份恢复策略:pg_basebackup、WAL 归档与 PITR 在企业 Java 系统中的落地
java·数据库·postgresql·pitr·pg_basebackup·wal 归档
摇滚侠4 小时前
Java 中如何表示负数,有符号和无符号的区别,补码的计算规则,char byte short int long 的范围
java·开发语言
流烟默4 小时前
SSE 实战:在 SpringBoot + Vue 项目中零依赖实现轻量级实时消息推送
vue.js·spring boot·后端·sse
奇牙coding4 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!4 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp