适用版本:MyBatis-Plus 3.5.3.1(最新稳定版)
适用场景:Java SpringBoot 企业级开发
核心定位:MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 基础上只做增强不做改变,极简封装单表 CRUD、条件构造、分页、逻辑删除等企业常用功能,彻底消灭冗余 XML/注解 SQL,提升开发效率。


MyBatis-Plus 核心概述

是什么?

MyBatis-Plus 是由国内开发者开源的 MyBatis 增强框架,无侵入式扩展 MyBatis 功能,解决原生 MyBatis 冗余 CRUD、硬编码字段、分页繁琐等痛点。

核心优势

  1. 零 SQL 实现单表 CURD,无需手写 XML
  2. 强大的条件构造器,支持 Lambda 语法(无硬编码)
  3. 内置分页、逻辑删除、自动填充、乐观锁等企业功能
  4. 支持主键雪花算法、UUID 等分布式策略
  5. 代码生成器一键生成 Entity/Mapper/Service/Controller

与原生 MyBatis 对比

功能原生 MyBatisMyBatis-Plus
单表 CRUD手写 SQL/XML继承接口直接用
条件查询硬编码字段名Lambda 方法引用
分页手动封装内置插件
逻辑删除手动写 SQL注解+配置
开发效率极高

SpringBoot 整合 MyBatis-Plus

核心依赖

创建 SpringBoot 项目,引入核心依赖pom.xml):

<!-- SpringBoot Web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 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>
<!-- Lombok 简化实体类 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

数据库表(样例表)

创建企业常用的 user 用户表:

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `username` varchar(32) NOT NULL COMMENT '用户名',
  `age` int DEFAULT NULL COMMENT '年龄',
  `email` varchar(64) DEFAULT NULL COMMENT '邮箱',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `del_flag` tinyint DEFAULT '0' COMMENT '逻辑删除 0-未删除 1-已删除',
  `version` int DEFAULT '0' COMMENT '乐观锁版本号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

配置文件(application.yml)

spring:
  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    # 开启日志(开发环境查看执行 SQL)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 别名包扫描(实体类包)
  type-aliases-package: com.example.mpdemo.entity

启动类配置

扫描 Mapper 接口包:

@SpringBootApplication
@MapperScan("com.example.mpdemo.mapper") // 扫描 Mapper 接口
public class MpDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpDemoApplication.class, args);
    }
}

基础 CRUD 语法(BaseMapper)

核心逻辑

MP 提供 BaseMapper<T> 接口,内置17 个单表 CRUD 方法,继承后直接使用,无需手写 SQL。

步骤

  1. 编写实体类
  2. Mapper 接口继承 BaseMapper<实体类>
  3. 直接调用 CRUD 方法

样例

实体类(User.java)
@Data // Lombok 生成 getter/setter/toString
@TableName("user") // 映射数据库表名(类名与表名一致可省略)
public class User {
    // 主键(自增)
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String username;
    private Integer age;
    private String email;
    private Date createTime;
    private Date updateTime;
    private Integer delFlag;
    private Integer version;
}
Mapper 接口(UserMapper.java)
// 继承 BaseMapper,泛型指定实体类
public interface UserMapper extends BaseMapper<User> {
    // 无需写任何方法,直接使用父类 CRUD
}
3.3.3 基础 CRUD 测试
@SpringBootTest
class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    // 1. 插入数据
    @Test
    void insert() {
        User user = new User();
        user.setUsername("张三");
        user.setAge(20);
        user.setEmail("zhangsan@qq.com");
        // insert 返回影响行数
        int rows = userMapper.insert(user);
        System.out.println("插入行数:" + rows);
        System.out.println("生成的主键:" + user.getId());
    }

    // 2. 根据 ID 查询
    @Test
    void selectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }

    // 3. 查询所有数据
    @Test
    void selectList() {
        List<User> userList = userMapper.selectList(null); // null 表示无条件
        userList.forEach(System.out::println);
    }

    // 4. 根据 ID 更新
    @Test
    void updateById() {
        User user = new User();
        user.setId(1L);
        user.setAge(21); // 只更新年龄
        userMapper.updateById(user);
    }

    // 5. 根据 ID 删除
    @Test
    void deleteById() {
        userMapper.deleteById(1L);
    }
}

BaseMapper 核心方法汇总

方法作用
insert(T entity)插入一条记录
selectById(Serializable id)根据 ID 查询
selectList(Wrapper queryWrapper)条件查询列表
updateById(T entity)根据 ID 更新
deleteById(Serializable id)根据 ID 删除
selectBatchIds(Collection idList)批量 ID 查询
delete(Wrapper queryWrapper)条件删除

条件构造器 Wrapper

核心逻辑

Wrapper 是 MP 的条件构造器抽象类,用于拼接 WHERE 条件,企业开发中最常用两个子类:

  • QueryWrapper<T>查询/删除 条件构造
  • UpdateWrapper<T>更新 条件构造

常用条件方法

方法含义示例
eq等于(=)eq(“age”, 20)
ne不等于(!=)ne(“username”, “张三”)
gt大于(>)gt(“age”, 18)
ge大于等于(>=)ge(“age”, 18)
lt小于(<)lt(“age”, 30)
le小于等于(<=)le(“age”, 30)
like模糊查询(%值%)like(“username”, “张”)
between区间查询between(“age”, 18, 30)
orderByAsc升序排序orderByAsc(“age”)
isNull字段为空isNull(“email”)

实战样例

QueryWrapper 查询
// 查询:年龄18-30岁、用户名包含"张"、邮箱不为空、按年龄升序
@Test
void queryWrapperTest() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age", 18, 30)
           .like("username", "张")
           .isNotNull("email")
           .orderByAsc("age");

    List<User> userList = userMapper.selectList(wrapper);
    userList.forEach(System.out::println);
}
UpdateWrapper 更新
// 更新:将用户名=张三的用户,年龄改为22
@Test
void updateWrapperTest() {
    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    wrapper.eq("username", "张三"); // 条件

    User user = new User();
    user.setAge(22); // 更新字段

    userMapper.update(user, wrapper);
}

缺点

硬编码数据库字段名(如 "age""username"),容易写错且不易维护,企业开发禁止使用普通 Wrapper


Lambda 条件构造器(推荐)

核心逻辑

解决普通 Wrapper 硬编码字段问题,使用 Lambda 方法引用 关联实体类字段,编译期校验,零错误。

核心子类:

  • LambdaQueryWrapper<T>:Lambda 查询
  • LambdaUpdateWrapper<T>:Lambda 更新

实战样例(对比普通 Wrapper)

// Lambda 查询:年龄18-30、用户名含"张"、邮箱非空
@Test
void lambdaQueryTest() {
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.between(User::getAge, 18, 30)
           .like(User::getUsername, "张")
           .isNotNull(User::getEmail)
           .orderByAsc(User::getAge);

    List<User> userList = userMapper.selectList(wrapper);
    userList.forEach(System.out::println);
}

// Lambda 更新:用户名=张三 -> 年龄22
@Test
void lambdaUpdateTest() {
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
    wrapper.eq(User::getUsername, "张三");

    User user = new User();
    user.setAge(22);
    userMapper.update(user, wrapper);
}

优势

  1. 方法引用关联实体字段,编译期报错(避免字段名写错)
  2. 代码可读性极高,符合企业开发规范
  3. 重构实体类字段时,自动同步修改

分页查询(必备)

核心逻辑

MP 内置分页插件,无需手写 limit,只需配置插件 + 传入 Page 对象,自动返回分页结果(总条数、总页数、当前页数据)。

步骤

  1. 配置分页插件
  2. 使用 selectPage(Page<T>, Wrapper) 方法

实战样例

分页插件配置(MyBatisPlusConfig.java)
@Configuration
public class MyBatisPlusConfig {
    /**
     * 注册 MyBatis-Plus 插件(分页+乐观锁)
     */
    @Bean
    public MyBatisPlusInterceptor myBatisPlusInterceptor() {
        MyBatisPlusInterceptor interceptor = new MyBatisPlusInterceptor();
        // 1. 添加分页插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        // 设置数据库类型(MySQL)
        paginationInterceptor.setDbType(DbType.MYSQL);
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}
分页查询测试
// 分页查询:第1页,每页3条,年龄>18,按ID降序
@Test
void pageTest() {
    // 1. 创建分页对象:current=当前页,size=每页条数
    Page<User> page = new Page<>(1, 3);
    // 2. 条件
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.gt(User::getAge, 18)
           .orderByDesc(User::getId);

    // 3. 执行分页查询
    Page<User> resultPage = userMapper.selectPage(page, wrapper);

    // 4. 分页结果
    System.out.println("当前页数据:" + resultPage.getRecords());
    System.out.println("总条数:" + resultPage.getTotal());
    System.out.println("总页数:" + resultPage.getPages());
    System.out.println("当前页:" + resultPage.getCurrent());
    System.out.println("每页条数:" + resultPage.getSize());
}

主键生成策略

核心逻辑

通过 @TableId(type = IdType.xxx) 指定主键生成策略,MP 提供 5 种主流策略,适配单机/分布式场景。

IdType 枚举详解

策略含义适用场景
AUTO数据库自增(依赖表 AUTO_INCREMENT)单机单库
ASSIGN_ID默认,雪花算法(Long 类型,全局唯一)分布式系统
ASSIGN_UUIDUUID 字符串(无中划线)字符串主键
INPUT手动输入主键自定义主键
NONE无策略,跟随全局默认配置

实战样例

@Data
@TableName("user")
public class User {
    // 分布式场景:雪花算法(默认可省略 type)
    // @TableId(type = IdType.ASSIGN_ID)
    // 单机场景:数据库自增
    @TableId(type = IdType.AUTO)
    private Long id;
}

常用字段映射注解

MP 提供注解解决实体类与数据库表映射不一致问题:

核心注解

  1. @TableName:映射表名
    @TableName("sys_user") // 类名User -> 表名sys_user
    
  2. @TableField:映射字段
    • value:字段名(实体字段与表字段不一致时)
    • exist = false:非数据库字段(实体类临时字段)
    • fill:自动填充(创建/更新时间)
    @TableField(value = "user_name") // 实体username -> 表字段user_name
    private String username;
    
    @TableField(exist = false) // 非数据库字段,MP 忽略
    private String tempData;
    

逻辑删除

核心逻辑

物理删除:直接删除数据库记录(不可恢复)
逻辑删除:更新 del_flag 字段(0=未删除,1=已删除),查询时自动过滤已删除数据,企业标准方案。

配置(application.yml)

mybatis-plus:
  global-config:
    db-config:
      # 逻辑删除字段名
      logic-delete-field: delFlag
      # 已删除值
      logic-delete-value: 1
      # 未删除值
      logic-not-delete-value: 0

实体类注解

@TableLogic // 标记逻辑删除字段
private Integer delFlag;

测试效果

调用 deleteById(1L),执行的 SQL 是:

UPDATE user SET del_flag=1 WHERE id=1 AND del_flag=0

调用 selectList(null),自动拼接 del_flag=0 条件。


自动填充(创建/更新时间)

核心逻辑

无需手动 setCreateTime()/setUpdateTime(),MP 自动在插入/更新时赋值,企业必备。

步骤

  1. 实体类标记填充策略
  2. 自定义元对象处理器

实战样例

实体类配置
@TableField(fill = FieldFill.INSERT) // 插入时填充
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE) // 插入/更新时填充
private Date updateTime;
元对象处理器(MyMetaObjectHandler.java)
@Component // 交给 Spring 管理
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时填充
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    // 更新时填充
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

乐观锁(并发控制)

核心逻辑

解决并发更新数据覆盖问题(如商品超卖),通过 version 版本号控制:

  1. 更新时携带版本号
  2. 版本号匹配才更新,同时版本号+1
  3. 不匹配则更新失败

配置

  1. 插件配置(已在分页插件中添加)
  2. 实体类标记 @Version
@Version
private Integer version;

Service 层封装

核心逻辑

MP 提供 IService<T> 接口 + ServiceImpl<M,T> 实现类,封装 Service 层 CRUD,无需手写基础方法。

实战样例

Service 接口
// 继承 IService,泛型=实体类
public interface UserService extends IService<User> {
    // 自定义业务方法
}
Service 实现类
// 继承 ServiceImpl,泛型=Mapper+实体类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 直接使用父类方法:save、saveBatch、getById、list、page 等
}
12.2.3 Service 常用方法
@Autowired
private UserService userService;

// 批量插入
@Test
void saveBatch() {
    List<User> list = new ArrayList<>();
    list.add(new User(null, "李四", 25, "lisi@qq.com", null, null, 0, 0));
    list.add(new User(null, "王五", 28, "wangwu@qq.com", null, null, 0, 0));
    userService.saveBatch(list); // 批量插入
}

// 根据ID查询
@Test
void getById() {
    User user = userService.getById(1L);
}

// 分页查询
@Test
void servicePage() {
    Page<User> page = new Page<>(1, 3);
    userService.page(page);
}

多表查询实现

MP 是单表增强框架,不推荐封装多表,企业开发多表查询用原生 MyBatis 手写 SQL

样例:用户+订单联查

自定义 VO(返回结果)
@Data
public class UserOrderVO {
    private Long userId;
    private String username;
    private Long orderId;
    private String orderNo;
}
Mapper 接口方法
public interface UserMapper extends BaseMapper<User> {
    // 多表查询:手写注解 SQL
    @Select("SELECT u.id user_id, u.username, o.id order_id, o.order_no " +
            "FROM user u LEFT JOIN `order` o ON u.id = o.user_id " +
            "WHERE u.id = #{userId}")
    UserOrderVO selectUserOrder(Long userId);
}

代码生成器(提升效率)

核心逻辑

MP 提供代码生成器,一键生成 Entity、Mapper、Service、Controller,企业开发提效 80%。

依赖

<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>

生成器代码(直接运行)

public class CodeGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus_demo", "root", "123456")
                // 全局配置
                .globalConfig(builder -> {
                    builder.author("企业开发师") // 作者
                           .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出路径
                           .enableSwagger() // 开启Swagger
                           .fileOverride(); // 覆盖文件
                })
                // 包配置
                .packageConfig(builder -> {
                    builder.parent("com.example.mpdemo") // 父包名
                           .entity("entity")
                           .mapper("mapper")
                           .service("service")
                           .controller("controller");
                })
                // 策略配置
                .strategyConfig(builder -> {
                    builder.addInclude("user") // 需要生成的表名
                           // Entity 策略
                           .entityBuilder()
                           .enableLombok()
                           .enableTableFieldAnnotation()
                           // Mapper 策略
                           .mapperBuilder()
                           .enableBaseResultMap()
                           // Service 策略
                           .serviceBuilder()
                           .formatServiceFileName("%sService")
                           // Controller 策略
                           .controllerBuilder()
                           .enableRestStyle();
                })
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }
}

实践要义

  1. 统一使用 Lambda 条件构造器,禁止硬编码字段名
  2. 所有表必须加 create_time/update_time/del_flag,开启自动填充+逻辑删除
  3. 分页查询必须配置插件,禁止手写 limit
  4. 分布式系统使用 ASSIGN_ID 雪花算法主键
  5. 多表查询手写原生 SQL,不依赖 MP 封装
  6. 批量操作使用 Service 层的 saveBatch/updateBatchById
  7. 生产环境关闭 SQL 日志,仅保留错误日志
  8. 禁止无条件调用 update/delete 方法(避免全表更新/删除)

总结

  1. 零SQL 实现单表 CRUD,大幅减少冗余代码
  2. Lambda 条件构造器解决硬编码痛点,提升代码质量
  3. 内置逻辑删除、自动填充、分页等企业功能,开箱即用
  4. 代码生成器+Service 封装,极致提升开发效率
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐