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 封装,极致提升开发效率
相关推荐
sjsjsbbsbsn4 小时前
向量数据库
数据库
逸Y 仙X4 小时前
文章十六:ElasticSearch 使用enrich策略实现大宽表
java·大数据·数据库·elasticsearch·搜索引擎·全文检索
Sherry Wangs4 小时前
MySQL 与向量数据库的核心区别:从结构化数据到语义搜索
数据库·mysql
@小柯555m4 小时前
MySql(高级操作符--高级操作符练习(2))
数据库·sql·mysql
凯尔萨厮4 小时前
Springboot2.x+JSP项目创建
java·数据库
Mr_linjw4 小时前
MySQL 中监控和优化慢 SQL & 索引小知识
数据库·sql·mysql
mftang4 小时前
BSS段、Data段、Text段的具体含义和数据特性
数据库·算法
码农阿豪4 小时前
Python 操作金仓数据库的完全指南(上篇):连接管理与高可用
开发语言·数据库·python
雾岛听风6914 小时前
Sql server
数据库·sql·sqlserver