手把手教你Mybatis-Plus :小白看完都能会,看完还不回找我,我给你补个蛋

Mybatis-Plus极速入门!!!

本博客学习资料:javafortest.oss-cn-beijing.aliyuncs.com/2025/08/01....

跟着我一步一步来,谁都能学会MP!!!

Mybatis-Plus(简称MP)是一个基于Mybatis框架的增强工具,它在Mybatis的基础上只做增强而不做改变,旨在简化开发、提高效率。Mybatis-Plus提供了一系列的功能和特性,使得开发人员能够更加高效地使用Mybatis进行数据库操作。

MP提供了一整套的 单表增删改查功能

官网地址:mybatis.plus/

2. MP入门

MP的使用步骤:

  1. 添加依赖坐标
  2. 准备实体类
  3. 创建一个空Mapper接口,继承BaseMapper<实体类>
  4. 可以调用Mapper的各种方法进行增删改查了

2.1 需求说明(以黑马程序员的Tlias智能学习辅助系统项目为例)

基于Mybatis-Plus,实现Tlias智能学习辅助系统部门管理的dao层所有数据库操作:

  1. 新增部门功能
  2. 根据id查询部门
  3. 根据id更新部门
  4. 根据id删除部门

2.2 准备环境

2.2.1 打开project

将资料中的初始代码-《mp-demo》拷贝到自己的代码目录中

连接MySQL数据库,执行资料里的《mp.sql》脚本

2.2.2 添加MP依赖

添加Mybatis-Plus的起步依赖,==已添加过了,不要重复添加==

xml 复制代码
<!--MybatisPlus起步依赖-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.7</version>
</dependency>
<!--MySQL驱动-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version>
</dependency>
2.2.2 修改数据库连接配置

修改application.yml,将其中的数据库连接信息修改成自己的

2.3 实现功能

2.3.1 准备实体类Dept

在主键字段对应的属性上添加注解:@TableId(value="主键字段名", type=IdType.AUTO)

less 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    //↓↓↓主键字段对应的属性上添加注解↓↓↓
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private String name;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}
2.3.2 定义Mapper接口
定义Mapper接口

创建DeptMapper接口,让其继承BaseMapper即可;且接口中所有方法都。最终代码如下:

csharp 复制代码
//不需要加@Mapper注解了,因为启动类上开启了全局的Mapper扫描
public interface DeptMapper extends BaseMapper<Dept> {
 
    //不需要自己写CURD功能方法了,因为继承了BaseMapper<Dept>就自动具备了dept表的CURD功能方法
}

为了简化单表CRUD,Mybatis-Plus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD,因此我们自定义的Mapper只要实现了这个BaseMapper,就无需自己实现单表CRUD了。

BaseMapper中已定义的方法有:

开启Mapper扫描

每个Mapper接口上需要添加@Mapper注解,Mapper接口才会被扫描到然后创建对象放到IoC容器中

也可以有更简便的方式:直接在启动类上添加注解:@MapperScan("mapper接口所在的包名") 自动动扫描Mapper

less 复制代码
@SpringBootApplication
@MapperScan(value = "com.itheima.mapper")
public class MpApplication {
    public static void main(String[] args) {
        SpringApplication.run(MpApplication.class, args);
    }
}
2.3.3 修改Service代码

将DeptServiceImpl里每个方法的功能都完成:调用mapper接口操作数据库

typescript 复制代码
@Service
public class DeptServiceImpl implements DeptService {
    //↓↓↓注入↓↓↓
    @Resource
    private DeptMapper deptMapper;
​
    @Override
    public List<Dept> queryAll() {
        //↓↓↓调用DeptMapper查询所有数据列表↓↓↓
        return deptMapper.selectList(null);
    }
​
    @Override
    public void deleteById(Integer id) {
        //↓↓↓调用DeptMapper根据id删除部门↓↓↓
        deptMapper.deleteById(id);
    }
​
    @Override
    public void add(Dept dept) {
        //补充数据
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        //↓↓↓调用DeptMapper执行insert↓↓↓
        deptMapper.insert(dept);
    }
​
    @Override
    public Dept findById(Integer id) {
        //↓↓↓调用DeptMapper根据id查询部门↓↓↓
        return deptMapper.selectById(id);
    }
​
    @Override
    public void updateById(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        //↓↓↓调用DeptMapper执行update语句↓↓↓
        deptMapper.updateById(dept);
    }
}

2.4 功能测试

打开APIfox测试各个功能是否正常

3. MP详解

3.1 MP常见注解

在刚刚的入门案例中,我们并没有在任何地方指定要操作的表,那么MP是怎么知道要操作哪张表呢?又怎么知道有哪些字段?哪个字段是主键呢?

3.1.1 MP的默认行为

我们的Mapper接口必须要继承一个BaseMapper<实体类>,MP就是根据其中的实体类名,通过反射进行推断的:

  • 推断表名:将实体类名 由驼峰规范转换为下划线规范,就是要操作的表名了。

    如果连上数据库后找不到这个表名,就会报错

    比如实体类名为Dept,则MP会会操作dept

  • 推断主键:MP默认将名称为id的属性,推断主键字段名为id

    如果连上数据库后,从表中找不到名称为id的字段,就会报错

  • 推断字段:MP默认将实体类里的属性名,由驼峰规范转换为下划线规范后作为字段名。并根据属性类型推断字段类型

    如果连上数据库后,从表中找不到这些字段,就会报错

有些情况下,MP的默认实现与实际场景不符,因此Mybatis-Plus提供了一些注解便于我们进行自定义配置

注解名 用法 作用
@TableName 加在实体类上 用于设置此实体类对应的表名
@TableId 加在主键属性上 用于设置此属性对应的主键字段,以及主键生成策略
@TableField 加在非主键属性上 用于设置此属性对应的字段
3.1.2 @TableName

表名注解,用在实体类上, 标识实体类所对应的表。示例:

less 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")  //表示此实体类对应的表名是tb_user
public class User {
    private Long id;
    private String name;
    private Boolean isMarried;
    private Integer order;
    private String address;
}
3.1.3 @TableId

主键注解,用在实体类的主键属性上,标识主键属性所对应的主键字段。

注解有2个属性参数:

  • value:非必须,用于配置 主键字段名

  • type:非必须,用于配置 主键生成策略,默认值是IdType.NONE。常用值有:

    取值 说明
    IdType.NONE 无,跟随全局
    IdType.AUTO★ 数据库主键自增
    IdType.ASSIGN_ID★ 分配ID(主键字段是数字或字符串类型时可用),默认由MP使用雪花算法自动生成主键值
    IdType.ASSIGN_UUID 分配UUID(主键字段是字符串类型时可用),默认由MP使用UUID算法生成随机字符串作为主键值
    IdType.INPUT★ 程序不处理主键,由开发人员手动设置主键值

示例:

less 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String name;
    private Boolean isMarried;
    private Integer order;
    private String address;
}
3.1.4 @TableField

普通字段注解,用在实体类里非主键属性上。通常情况下不需要使用@TableField注解,以下特殊情况除外:

  • 实体类里的属性名与字段名不一致时:需要为MP指定对应的字段名
  • 实体类里的属性名以is开头时:例如isMarried,MP会去掉is作为字段名,这会导致属性与字段名不一致,因为需要指定字段名
  • 实体类里的属性名与数据库一致,但与数据库的关键字冲突时:需要为字段名添加转义字段
  • 实体类里的属性 没有对应的字段存在时
less 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
​
    @TableField("username")//属性名与字段名不一致,因为需要指定字段名
    private String uname;
​
    @TableField("is_married")//不同版本的MP,对is_xxx名称的字段不同的处理,所以需要我们明确指定字段名
    private Boolean isMarried;
​
    @TableField("`order`") //order是MySQL里的关键字,因为要使用转义字符``将字段名括起来
    private Integer order;
​
    @TableField(exist = false)//MP将不会去数据库表里找此属性对应的字段
    private String address;
}

3.2 MP常见配置

Mybatis-Plus也支持一些全局的自定义配置,详见官方文档:www.baomidou.com/reference/

大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:

yaml 复制代码
mybatis-plus:
  mapper-locations: classpath:mapper/**/*Mapper.xml  #指定xml映射文件的位置
  global-config:
    db-config:
      id-type: auto # 全局id类型为自增长
      update-strategy: not_null # 更新策略:只更新非空字段
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.3 测试效果

运行单元测试类,验证一下能否正常查询tb_user表的数据

4. 小结

MP的使用步骤:

  1. 先添加MP的依赖坐标;准备实体类
  2. 创建空的Mapper接口,继承BaseMapper<实体类>

MP的BaseMapper提供了一整套的单表CURD功能方法:

  • 所有查询方法:以select开头
  • 所有插入方法:以insert开头
  • 所有修改方法:以update开头
  • 所有删除方法:以delete开头

MP的默认情况:

  • MP怎么知道表名的:根据实体类名推断,将实体类名由驼峰转换成下划线,就认为是表名了
  • MP怎么知道主键字段:默认将id作为主键名称
  • MP怎么知道字段名称:根据实体类的属性名,将属性名由驼峰转换成下划线,就认为是字段名了

MP的实体类名与表名如果不匹配:给实体类加@TableName("表名")

MP的属性名与字段名如果不匹配:给属性加@TableField(value="字段名")

  • 以is开头的属性名要加@TableField(value="字段名")

  • 属性名与字段名完全不匹配的要加@TableField(value="字段名")

  • 属性名没有对应的字段存在时要加@TableField(exist=false)

  • 属性名是SQL语句里的关键字要加

    ini 复制代码
    @TableField(value="`字段名`")

MP的主键字段要加@TableId(value="主键字段名", type=IdType.主键生成策略)

  • AUTO★:自增。Java程序不需要为主键属性赋值,MP也不会为主键字段设置值,由数据库自增生成主键值
  • ASSIGN_ID★:雪花算法,生成一个Long数字。Java程序不需要为主键属性赋值,MP会利用雪花算法生成主键值,数据库直接保存
  • ASSIGN_UUID:UUID算法,生成一个随机字符串。Java程序不需要为主键属性赋值,MP会生成UUID随机字符串,数据库直接保存
  • NONE:不设置,跟随全局。
  • INPUT★:必须由开发人员在Java程序里为主键属性赋值,MP不处理、数据库直接保存

三、MP核心功能

刚才的案例中都是以id为条件的简单CRUD,一些复杂条件的SQL语句就要用到一些更高级的功能了,所以,接下来咱们开始学习Mybatis-Plus中的核心功能,包括2个小节:

  • 条件构造器
  • IService接口

1. 条件构造器

1.1 BaseMapper的条件操作方法

除了查询以外,修改、删除的SQL语句都需要指定where条件。

因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件:

1.2 Wrapper说明

1.2.1 Wrapper的继承体系

参数中的Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图:

1.2.2 Wrapper对象的创建方式

方式1:使用new创建

ini 复制代码
QueryWrapper<实体类> wrapper = new QueryWrapper<>();

方式2:使用Wrappers的静态方法创建

ini 复制代码
QueryWrapper<实体类> wrapper = Wrappers.<实体类>query();
1.2.3 Wrapper的常用方法

Wrapper的子类AbstractWrapper提供了where中包含的所有条件构造方法:

而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:

而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:

1.3 Wrapper使用示例

1.3.1 准备基础代码

我们以emp表为例进行演示,所以需要先准备EmpMapper

java 复制代码
@Mapper
public interface EmpMapper extends BaseMapper<Emp> {
​
}

再准备单元测试类:在src\test\java下的包com.itheima里创建测试类

kotlin 复制代码
@SpringBootTest
public class QueryWrapperTest {
​
    @Autowired
    private EmpMapper empMapper;
    
}
1.3.2 QueryWrapper用法

无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。

查询

要求:查询姓名中包含"李"且薪资大于等于5000的员工的 id, name, phone, salary字段。

在测试类里增加测试方法,代码如下:

csharp 复制代码
@Test
public void testQueryWrapper() {
    // 查询姓名中包含"李"且薪资大于等于5000的员工的 id, name, phone, salary字段
    QueryWrapper<Emp> queryWrapper = Wrappers.<Emp>query()
        .like("name", "李")
        .ge("salary", 5000)
        .select("id", "name", "phone", "salary");
​
    List<Emp> emps = empMapper.selectList(queryWrapper);
    emps.forEach(System.out::println);
}
更新

要求:更新名为"李忠"的员工的薪水为9000。在测试类里增加测试方法,代码如下:

ini 复制代码
@Test
public void testUpdateByQueryWrapper() {
    // 更新名为"李忠"的员工的薪水为9000
    Emp emp = new Emp();
    emp.setSalary(9000);
    QueryWrapper<Emp> queryWrapper = Wrappers.<Emp>query().eq("name", "李忠");
    empMapper.update(emp, queryWrapper);
}
1.3.2 UpdateWrapper用法

要求:更新id为5, 6, 7的员工的薪水加2000,密码重置为12345。

SQL:update emp set salary = salary + 2000, password = '12345' where id in (5, 6, 7);

在测试类里增加测试方法,代码如下:

csharp 复制代码
@Test
public void testUpdateByQueryWrapper(){
    UpdateWrapper<Emp> wrapper = Wrappers.<Emp>update()
        .set("password", "12345")
        .setSql("salary = salary + 2000")
        .in("id", 5, 6, 7);
    empMapper.update(wrapper);
}

1.4 LambdaXxxWrapper

无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,存在硬编码问题,在编程规范中是不推荐的。

MP又提供了一套基于Lambda的Wrapper,是基于成员变量的get方法和反射技术推断出字段名的。我们只需要提供成员变量的get方法给MP即可:

  • LambdaQueryWrapper:对应QueryWrapper
  • LambdaUpdateWrapper:对应UpdateWrapper

用法示例:

scss 复制代码
@Test
public void testLambdaQueryWrapper() {
    // 查询姓名中包含"李"且薪资大于等于5000的员工的 id, name, phone, salary字段
    LambdaQueryWrapper<Emp> queryWrapper = Wrappers.<Emp>lambdaQuery()
            .like(Emp::getName, "李")
            .gt(Emp::getSalary, 5000)
            .select(Emp::getId, Emp::getName, Emp::getPhone, Emp::getSalary);
​
    List<Emp> emps = empMapper.selectList(queryWrapper);
    System.out.println(emps);
}
​
@Test
public void testLambdaUpdateWrapper() {
    // 更新id为5, 6, 7的员工的薪水,加2000
    LambdaUpdateWrapper<Emp> updateWrapper = Wrappers.<Emp>lambdaUpdate()
            .in(Emp::getId, 5, 6, 7)
            .setSql("salary = salary + 2000");
    empMapper.update(updateWrapper);
}

1.5 小结

怎么创建QueryWrapper条件对象:

  • 方式1:new QueryWrapper<实体类>()
  • 方式2:Wrappers.<实体类>query()

为QueryWrapper对象添加where条件:

  • 字段=值:eq("字段名", 值)
  • 字段>值:gt("字段名", 值)
  • 字段<值:lt("字段名", 值)
  • 字段>=值:ge("字段名", 值)
  • 字段<=值:le("字段名", 值)
  • 字段!=值:ne("字段名", 值)
  • 字段 in (值,..): in("字段名", 值, 值,....)
  • 字段 like '%值%':like("字段名", 值)
  • 字段 between 开始 and 结束: between("字段名", 开始, 结束)

为QueryWrapper对象在查询时添加select的字段:

  • select(字段,字段,...)

怎么创建UpdateWrapper条件对象:用于拼接update语句里的set和where

  • 方式1:new UpdateWrapper<实体类>()
  • 方式2:Wrappers.<实体类>update()

UpdateWrapper对象添加set子句:

  • set(字段, 值)
  • setSQL("SQL片段")

UpdateWrapper对象添加where子句:略

使用LambdaQueryWrapper:

  • 好处:如果写错了会直接报错提示,可以更早发现问题;不需要写字段名了,而是写JavaBean的get方法

  • 创建:

    方式1:new LambdaQueryWrapper<实体类>()

    方式2:Wrappers.<实体类>lambdaQuery()

使用LambdaUpdateWrapper:

  • 创建:

    方式1:new LambdaUpdateWrapper<实体类>()

    方式2:Wrappers.<实体类>lambdaUpdate()

2. IService接口

Mybatis-Plus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。

通用接口为IService,默认实现为ServiceImpl,其中封装的方法可以分为以下几类:

  • save:新增
  • remove:删除
  • update:更新
  • get:查询单个结果
  • list:查询集合结果
  • count:计数
  • page:分页查询

2.1 API说明

2.1.1 新增方法
  • save是新增单个元素
  • saveBatch是批量新增
  • saveOrUpdate是根据id判断,如果数据存在就更新,不存在则新增
  • saveOrUpdateBatch是批量的新增或修改
2.1.2 删除方法
  • removeById:根据id删除
  • removeByIds:根据id批量删除
  • removeByMap:根据Map中的键值对条件删除
  • remove(Wrapper<T>):根据Wrapper条件删除
2.1.3 修改方法
  • updateById:根据id修改
  • update(Wrapper<T>):根据UpdateWrapper修改,Wrapper中包含setwhere部分
  • update(T,Wrapper<T>):按照T内的数据修改与Wrapper匹配到的数据
  • updateBatchById:根据id批量修改
2.4.1 查询方法
GET方法
  • getById:根据id查询1条数据
  • getOne(Wrapper<T>):根据Wrapper查询1条数据
  • getBaseMapper:获取Service内的BaseMapper实现,某些时候需要直接调用Mapper内的自定义SQL时可以用这个方法获取到Mapper
List方法
  • listByIds:根据id批量查询
  • list(Wrapper<T>):根据Wrapper条件查询多条数据
  • list():查询所有
Count方法
  • count():统计所有数量
  • count(Wrapper<T>):统计符合Wrapper条件的数据数量

2.2 使用示例

2.2.1 用法说明
  • 我们自定义的Service接口,继承IService<实体类>父接口
  • 我们自定义的Service实现类,继承ServiceImpl<XxxMapper, 实体类>父类
2.2.2 用法示例
需求说明

基于Mybatis-Plus的 Iservice 接口,实现Tlias智能学习辅助系统部门管理页面的所有功能:

  • 新增部门功能
  • 根据id查询部门
  • 根据id更新部门
  • 根据id删除部门
修改DeptService接口

让DeptService接口继承IService<Dept>,然后删除接口里所有的方法。最终接口如下:

csharp 复制代码
public interface DeptService extends IService<Dept> {
​
}
修改DeptServiceImpl类

让DeptServiceImpl类继承ServiceImpl<DeptMapper, Dept>,然后删除类里所有的方法。最终代码如下:

scala 复制代码
@Service
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements DeptService {
​
}
修改DeptController类

修改一下DeptController调用DeptService的代码:

less 复制代码
@Slf4j
@RestController
@RequestMapping("/depts")
public class DeptController {
​
    @Resource
    private DeptService deptService;
​
    @GetMapping
    public Result queryAll(){
        log.info("查询所有部门");
​
        // ↓↓↓修改代码,调用service的list()方法↓↓↓
        List<Dept> deptList = deptService.list();
        return Result.success(deptList);
    }
​
    @DeleteMapping
    public Result deleteById(Integer id){
        log.info("根据id删除部门,id={}", id);
        // ↓↓↓修改代码,调用service的removeById方法↓↓↓
        deptService.removeById(id);
        return Result.success();
    }
​
    @PostMapping
    public Result add(@RequestBody Dept dept){
        log.info("新增部门,dept={}", dept);
        // ↓↓↓修改代码,调用service的save方法↓↓↓
        deptService.save(dept);
        return Result.success();
    }
​
    @GetMapping("/{id}")
    public Result findById(@PathVariable Integer id){
        log.info("根据id查询部门,id={}", id);
        // ↓↓↓修改代码,调用service的getById方法↓↓↓
        Dept dept = deptService.getById(id);
        return Result.success(dept);
    }
​
    @PutMapping
    public Result update(@RequestBody Dept dept){
        log.info("修改部门,dept={}", dept);
        // ↓↓↓修改代码,调用service的updateById方法↓↓↓
        deptService.updateById(dept);
        return Result.success();
    }
}
功能测试

打开页面,重新验证部门管理的功能是否正常

觉得有用的话,点赞收藏就是对硬核干货最好的认可~谢谢啦~

相关推荐
coding随想4 分钟前
网络层的“四骑士”:深入浅出IP、ICMP、ARP、RARP协议
后端·网络协议
sino爱学习5 分钟前
基于Redis 发布订阅实现一个轻量级本地缓存刷新
后端
kushu717 分钟前
Java 包
java·开发语言
bug菌18 分钟前
还在为编程效率发愁?字节跳动Trae如何让你秒变“代码大师“!
后端·ai编程·trae
Moonbit22 分钟前
MoonBit Perals Vol.04: 用MoonBit 探索协同式编程
后端·程序员·编程语言
2501_9096867023 分钟前
基于SpringBoot的旅游网站系统
vue.js·spring boot·后端
HZ_YZ25 分钟前
服务器docker部署项目
后端
用户849210736938038 分钟前
Skywalking 部署
后端
bug菌38 分钟前
🤔领导突然考我Spring中的注解@Bean,它是做什么用的?我...
java·后端·spring
JavaArchJourney1 小时前
ArrayList 源码分析
java