MyBatis-Plus:MyBatis的进阶开发

适用版本 :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 对比

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

SpringBoot 整合 MyBatis-Plus

核心依赖

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

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 用户表:

sql 复制代码
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)

yaml 复制代码
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 接口包:

java 复制代码
@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)
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)
java 复制代码
// 继承 BaseMapper,泛型指定实体类
public interface UserMapper extends BaseMapper<User> {
    // 无需写任何方法,直接使用父类 CRUD
}
3.3.3 基础 CRUD 测试
java 复制代码
@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 查询
java 复制代码
// 查询:年龄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 更新
java 复制代码
// 更新:将用户名=张三的用户,年龄改为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)

java 复制代码
// 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)
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;
    }
}
分页查询测试
java 复制代码
// 分页查询:第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_UUID UUID 字符串(无中划线) 字符串主键
INPUT 手动输入主键 自定义主键
NONE 无策略,跟随全局 默认配置

实战样例

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

常用字段映射注解

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

核心注解

  1. @TableName :映射表名

    java 复制代码
    @TableName("sys_user") // 类名User -> 表名sys_user
  2. @TableField :映射字段

    • value:字段名(实体字段与表字段不一致时)
    • exist = false:非数据库字段(实体类临时字段)
    • fill:自动填充(创建/更新时间)
    java 复制代码
    @TableField(value = "user_name") // 实体username -> 表字段user_name
    private String username;
    
    @TableField(exist = false) // 非数据库字段,MP 忽略
    private String tempData;

逻辑删除

核心逻辑

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

配置(application.yml)

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

实体类注解

java 复制代码
@TableLogic // 标记逻辑删除字段
private Integer delFlag;

测试效果

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

sql 复制代码
UPDATE user SET del_flag=1 WHERE id=1 AND del_flag=0

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


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

核心逻辑

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

步骤

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

实战样例

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

@TableField(fill = FieldFill.INSERT_UPDATE) // 插入/更新时填充
private Date updateTime;
元对象处理器(MyMetaObjectHandler.java)
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
java 复制代码
@Version
private Integer version;

Service 层封装

核心逻辑

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

实战样例

Service 接口
java 复制代码
// 继承 IService,泛型=实体类
public interface UserService extends IService<User> {
    // 自定义业务方法
}
Service 实现类
java 复制代码
// 继承 ServiceImpl,泛型=Mapper+实体类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 直接使用父类方法:save、saveBatch、getById、list、page 等
}
12.2.3 Service 常用方法
java 复制代码
@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(返回结果)
java 复制代码
@Data
public class UserOrderVO {
    private Long userId;
    private String username;
    private Long orderId;
    private String orderNo;
}
Mapper 接口方法
java 复制代码
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%。

依赖

xml 复制代码
<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>

生成器代码(直接运行)

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("企业开发师") // 作者
                           .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 封装,极致提升开发效率
相关推荐
zxrhhm几秒前
Oracle检查点Checkpoint深度解析
数据库·oracle
rising start2 分钟前
三、深入理解MySQL索引底层
数据库·mysql
weixin_426150709 分钟前
AI辅助Oracle容量规划:告别拍脑袋扩容
运维·数据库·人工智能·oracle
l1t10 分钟前
DeepSeek总结的PostgreSQL 表访问方法
数据库·postgresql
数据库小学妹11 分钟前
CTE+阶段式递归:用公共表表达式搞定复杂业务逻辑,告别SQL难题!
数据库·经验分享·b树·sql
UtopianCoding13 分钟前
数据库语法对比详细规则
数据库·mysql·gaussdb
KaMeidebaby13 分钟前
卡梅德生物技术快报|多肽库筛选:基于全质粒 PCR 的噬菌体文库构建与小分子表位淘选实战
前端·数据库·其他·百度·新浪微博
phltxy20 分钟前
Redis 常见面试题
数据库·redis·缓存
IpdataCloud20 分钟前
IP查询工具怎么选?在线API vs IP离线库:精度、速度、成本、隐私全对比
服务器·网络·数据库
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ21 分钟前
MySQL选择字符集和排序规则
数据库·mysql