MyBatis-Plus 使用指南

MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 基础上只做增强不做改变,简化 CRUD 操作、提供分页、逻辑删除、乐观锁等实用功能。本文涵盖 MP 从入门到高级特性的全维度使用教程。

一、环境准备

1. 核心依赖(Maven)

适用于 Spring Boot 项目(最常用),非 Spring Boot 项目需额外配置 MyBatis 核心依赖和 MP 适配包。

xml

复制代码
<!-- Spring Boot 父依赖(可选,用于版本管理) -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.15</version>
    <relativePath/>
</parent>

<!-- 核心依赖 -->
<dependencies>
    <!-- MyBatis-Plus 核心包 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version>
    </dependency>
    <!-- MySQL 驱动(根据数据库类型替换) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
        <scope>runtime</scope>
    </dependency>
    <!-- Spring Boot 测试(可选) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Lombok(可选,简化实体类) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 分页插件依赖(MP 3.4.0+ 内置,无需额外引入) -->
</dependencies>

2. 配置文件(application.yml)

yaml

复制代码
spring:
  # 数据库配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root

# MyBatis-Plus 配置
mybatis-plus:
  # 实体类别名包扫描
  type-aliases-package: com.example.mp.entity
  # Mapper XML 文件位置
  mapper-locations: classpath:mapper/**/*.xml
  # 配置日志(可选,方便调试)
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 下划线转驼峰(默认开启,可省略)
    map-underscore-to-camel-case: true
  # 全局配置
  global-config:
    db-config:
      # 主键类型:AUTO(数据库自增)、NONE(无策略)、INPUT(手动输入)、ASSIGN_ID(雪花算法)、ASSIGN_UUID(UUID)
      id-type: ASSIGN_ID
      # 逻辑删除字段名(默认 deleted)
      logic-delete-field: isDeleted
      # 逻辑删除-未删除值(默认 0)
      logic-not-delete-value: 0
      # 逻辑删除-已删除值(默认 1)
      logic-delete-value: 1
      # 表名前缀(可选,例如表名 t_user,实体类 User)
      # table-prefix: t_

3. 启动类注解

添加 @MapperScan 扫描 Mapper 接口:

java

运行

复制代码
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

二、核心使用

1. 实体类(Entity)

使用 MP 注解配置数据库映射关系:

java

运行

复制代码
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;

@Data // Lombok 注解,简化get/set
@TableName("user") // 指定数据库表名(默认类名转下划线)
public class User {
    // 主键策略:ASSIGN_ID(雪花算法)、AUTO(自增)
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    // 字段名(默认属性名转下划线,若一致可省略)
    @TableField("user_name")
    private String userName;

    private Integer age;

    private String email;

    // 逻辑删除字段(需配合全局配置)
    @TableLogic
    private Integer isDeleted;

    // 自动填充:创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    // 自动填充:更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    // 乐观锁字段
    @Version
    private Integer version;

    // 忽略字段(不映射到数据库)
    @TableField(exist = false)
    private String ignoreField;
}

2. Mapper 接口

继承 BaseMapper 即可获得 CRUD 方法,无需编写 XML:

java

运行

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

@Mapper // 或通过启动类@MapperScan扫描
public interface UserMapper extends BaseMapper<User> {
    // 自定义SQL可在此编写,例如:
    // List<User> selectByAge(Integer age);
}

3. Service 层(可选,推荐)

MP 提供 IServiceServiceImpl 封装批量操作、分页等高级功能:

java

运行

复制代码
// Service 接口
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.mp.entity.User;

public interface UserService extends IService<User> {
    // 自定义业务方法
}

// Service 实现类
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 实现自定义业务方法
}

4. 基础 CRUD 操作

(1)Mapper 层示例

java

运行

复制代码
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    // 新增
    @Test
    public void testInsert() {
        User user = new User();
        user.setUserName("张三");
        user.setAge(20);
        user.setEmail("zhangsan@test.com");
        int rows = userMapper.insert(user); // 返回受影响行数
        System.out.println("新增成功,ID:" + user.getId());
    }

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

    // 条件查询
    @Test
    public void testSelectList() {
        // QueryWrapper 构造查询条件
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("age", 20) // 等于
               .like("user_name", "张") // 模糊查询
               .orderByDesc("create_time"); // 排序
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }

    // 更新(根据ID)
    @Test
    public void testUpdateById() {
        User user = new User();
        user.setId(1L);
        user.setAge(21);
        int rows = userMapper.updateById(user);
        System.out.println("更新行数:" + rows);
    }

    // 删除(根据ID,逻辑删除)
    @Test
    public void testDeleteById() {
        int rows = userMapper.deleteById(1L);
        System.out.println("删除行数:" + rows);
    }
}
(2)Service 层示例(推荐)

Service 层封装了更便捷的批量操作、分页等:

java

运行

复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.entity.User;
import com.example.mp.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class UserServiceTest {
    @Autowired
    private UserService userService;

    // 批量新增
    @Test
    public void testSaveBatch() {
        User user1 = new User();
        user1.setUserName("李四");
        user1.setAge(22);
        User user2 = new User();
        user2.setUserName("王五");
        user2.setAge(23);
        boolean success = userService.saveBatch(List.of(user1, user2), 100); // 批量大小100
        System.out.println("批量新增是否成功:" + success);
    }

    // 分页查询
    @Test
    public void testPage() {
        // 构造分页对象(页码,每页条数)
        Page<User> page = new Page<>(1, 10);
        // 构造条件(LambdaQueryWrapper 避免硬编码字段名)
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
               .gt(User::getAge, 20) // 年龄大于20
               .orderByDesc(User::getUpdateTime);
        // 分页查询
        IPage<User> userPage = userService.page(page, wrapper);
        System.out.println("总条数:" + userPage.getTotal());
        System.out.println("总页数:" + userPage.getPages());
        userPage.getRecords().forEach(System.out::println);
    }

    // 条件查询(Lambda)
    @Test
    public void testLambdaQuery() {
        List<User> userList = userService.list(new LambdaQueryWrapper<User>()
               .eq(User::getEmail, "zhangsan@test.com")
               .isNull(User::getUpdateTime));
        System.out.println(userList);
    }

    // 批量删除
    @Test
    public void testRemoveBatchByIds() {
        boolean success = userService.removeBatchByIds(List.of(1L, 2L));
        System.out.println("批量删除是否成功:" + success);
    }
}

三、高级特性

1. 分页插件

MP 3.4.0+ 分页插件配置简化,需配置拦截器:

java

运行

复制代码
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 {
    /**
     * 分页插件(支持MySQL、Oracle等)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页拦截器(指定数据库类型)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

2. 乐观锁插件

用于解决并发更新问题,步骤:

  1. 实体类添加 @Version 注解(字段类型推荐 Integer/Long);
  2. 配置乐观锁拦截器:

java

运行

复制代码
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁拦截器
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 分页拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
  1. 使用示例:

java

运行

复制代码
@Test
public void testOptimisticLocker() {
    // 1. 查询数据(获取当前版本号)
    User user = userService.getById(1L);
    // 2. 更新数据(版本号会自动+1)
    user.setAge(25);
    boolean success = userService.updateById(user);
    System.out.println("更新是否成功:" + success);
}

3. 自动填充

实现字段(如 createTime、updateTime)自动填充,步骤:

  1. 实体类字段添加 @TableField(fill = FieldFill.INSERT/INSERT_UPDATE)
  2. 实现填充处理器:

java

运行

复制代码
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 新增时填充
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    // 更新时填充
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

4. 逻辑删除

已在全局配置中配置,核心:

  • 实体类字段添加 @TableLogic(或全局配置 logic-delete-field);
  • 删除操作(deleteById/removeById)会转为更新逻辑删除字段;
  • 查询操作(selectList/list)会自动拼接 WHERE is_deleted=0

5. 条件构造器(Wrapper)

MP 提供多种 Wrapper 构造查询条件,推荐使用 Lambda 版本避免硬编码:

类型 说明 推荐场景
QueryWrapper 普通条件构造器 简单条件、字段名明确
LambdaQueryWrapper Lambda 条件构造器 避免字段名硬编码
UpdateWrapper 更新条件构造器 更新操作(含条件)
LambdaUpdateWrapper Lambda 更新条件构造器 更新操作(避免硬编码)

示例:LambdaUpdateWrapper

java

运行

复制代码
@Test
public void testUpdateWrapper() {
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>()
           .eq(User::getId, 1L)
           .set(User::getAge, 30)
           .set(User::getEmail, "update@test.com");
    boolean success = userService.update(wrapper);
    System.out.println("更新是否成功:" + success);
}

6. 自定义 SQL

当 MP 内置方法满足不了需求时,可自定义 SQL:

(1)Mapper 接口定义方法

java

运行

复制代码
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.example.mp.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;

public interface UserMapper extends BaseMapper<User> {
    // 方式1:注解式 SQL
    @Select("SELECT * FROM user WHERE age > #{age} ${ew.customSqlSegment}")
    List<User> selectByAge(@Param("age") Integer age, @Param(Constants.WRAPPER) Wrapper<User> wrapper);

    // 方式2:XML 式 SQL(推荐复杂SQL)
    List<User> selectByEmail(String email);
}
(2)XML 文件(resources/mapper/UserMapper.xml)

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mp.mapper.UserMapper">
    <select id="selectByEmail" resultType="com.example.mp.entity.User">
        SELECT * FROM user WHERE email = #{email}
    </select>
</mapper>
(3)使用示例

java

运行

复制代码
@Test
public void testCustomSql() {
    // 注解式 SQL
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().like(User::getUserName, "张");
    List<User> userList1 = userMapper.selectByAge(20, wrapper);
    
    // XML 式 SQL
    List<User> userList2 = userMapper.selectByEmail("zhangsan@test.com");
}

7. 多数据源

MP 整合动态多数据源(基于 dynamic-datasource-spring-boot-starter):

(1)添加依赖

xml

复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>
(2)配置多数据源

yaml

复制代码
spring:
  datasource:
    dynamic:
      primary: master # 默认数据源
      strict: false # 非严格模式(不存在数据源时抛异常)
      datasource:
        master: # 主库
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/mp_master?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
          username: root
          password: root
        slave: # 从库
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/mp_slave?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
          username: root
          password: root
(3)使用注解指定数据源

java

运行

复制代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 使用主库
    @DS("master")
    public boolean saveUser(User user) {
        return save(user);
    }

    // 使用从库
    @DS("slave")
    public List<User> listUser() {
        return list();
    }
}

8. 代码生成器

MP 提供 AutoGenerator 自动生成 Entity、Mapper、Service、Controller 代码:

(1)添加依赖

xml

复制代码
<!-- 代码生成器 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.5</version>
</dependency>
<!-- 模板引擎(Freemarker) -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>
(2)生成代码示例

java

运行

复制代码
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;

public class CodeGenerator {
    public static void main(String[] args) {
        // 数据库连接配置
        String url = "jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
        String username = "root";
        String password = "root";

        FastAutoGenerator.create(url, username, password)
                // 全局配置
               .globalConfig(builder -> {
                    builder.author("yourname") // 作者
                           .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
                           .enableSwagger() // 启用Swagger(需添加swagger依赖)
                           .commentDate("yyyy-MM-dd") // 注释日期
                           .disableOpenDir(); // 生成后不打开目录
                })
                // 包配置
               .packageConfig(builder -> {
                    builder.parent("com.example.mp") // 父包名
                           .moduleName("") // 模块名
                           .entity("entity") // 实体类包名
                           .mapper("mapper") // Mapper包名
                           .service("service") // Service包名
                           .controller("controller") // Controller包名
                           .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir") + "/src/main/resources/mapper")); // Mapper XML路径
                })
                // 策略配置
               .strategyConfig(builder -> {
                    builder.addInclude("user") // 生成的表名
                           .addTablePrefix("t_") // 忽略表前缀
                           .entityBuilder() // 实体类策略
                               .enableLombok() // 启用Lombok
                               .enableTableFieldAnnotation() // 启用字段注解
                               .idType(com.baomidou.mybatisplus.annotation.IdType.ASSIGN_ID) // 主键策略
                           .mapperBuilder() // Mapper策略
                               .enableBaseResultMap() // 启用BaseResultMap
                               .enableBaseColumnList() // 启用BaseColumnList
                           .serviceBuilder() // Service策略
                               .formatServiceFileName("%sService") // Service命名规则
                               .formatServiceImplFileName("%sServiceImpl") // ServiceImpl命名规则
                           .controllerBuilder() // Controller策略
                               .enableRestStyle() // 启用RestController
                               .formatFileName("%sController"); // Controller命名规则
                })
                // 模板引擎
               .templateEngine(new FreemarkerTemplateEngine())
                // 执行生成
               .execute();
    }
}

四、常见问题

1. 主键自增不生效

  • 数据库表主键需设置为自增;
  • 实体类 @TableId 注解 type 设为 IdType.AUTO
  • 全局配置 id-type 设为 AUTO

2. 分页查询总条数为 0

  • 确保分页插件已配置;
  • 检查 SQL 是否有分组(GROUP BY),分组查询需手动统计总条数;
  • 确认表名、字段名映射正确。

3. 逻辑删除后查询不到数据

  • 逻辑删除字段类型需与全局配置一致(如 Integer);

  • 查询时 MP 会自动拼接 WHERE is_deleted=0,若需查询已删除数据,需手动拼条件:

    java

    运行

    复制代码
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
           .eq(User::getIsDeleted, 1); // 查询已删除数据

4. 乐观锁更新失败

  • 确保实体类添加 @Version 注解;
  • 乐观锁拦截器已配置;
  • 更新前需先查询获取当前版本号,否则版本号为 null 会更新失败。

五、总结

MyBatis-Plus 核心优势是简化开发,通过封装 CRUD、分页、逻辑删除等功能,减少重复代码。核心要点:

  1. 基础使用:继承 BaseMapper/IService 快速实现 CRUD;
  2. 高级特性:分页、乐观锁、自动填充、逻辑删除提升开发效率;
  3. 自定义扩展:通过 Wrapper、自定义 SQL 满足复杂业务需求;
  4. 工程化:代码生成器、多数据源适配企业级开发。
相关推荐
I'm Jie1 小时前
告别重复编码!SpringBoot 字段变更(新旧值)日志工具类的规范化设计与优雅实现
java·spring boot·后端
哥谭居民00011 小时前
需求分析,领域划分到选择套用业务模式到转化落地,两个基本案例
java·大数据·需求分析
Tao____1 小时前
适合中小型项目的物联网平台
java·物联网·mqtt·开源·iot
小马爱打代码1 小时前
Spring AI:多模态 AI 大模型
java·人工智能·spring
李贺梖梖1 小时前
day07 方法、面向对象1
java
除了代码啥也不会1 小时前
Java基于SSE流式输出实战
java·开发语言·交互
虹科网络安全1 小时前
艾体宝干货 | Redis Java 开发系列#2 数据结构
java·数据结构·redis
sg_knight1 小时前
SSE 技术实现前后端实时数据同步
java·前端·spring boot·spring·web·sse·数据同步
Slow菜鸟2 小时前
Java项目基础架构(二)| 通用响应与异常
java·开发语言