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的使用步骤:
- 添加依赖坐标
- 准备实体类
- 创建一个空Mapper接口,继承
BaseMapper<实体类>
- 可以调用Mapper的各种方法进行增删改查了
2.1 需求说明(以黑马程序员的Tlias智能学习辅助系统项目为例)
基于Mybatis-Plus,实现Tlias智能学习辅助系统部门管理的dao层所有数据库操作:
- 新增部门功能
- 根据id查询部门
- 根据id更新部门
- 根据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的使用步骤:
- 先添加MP的依赖坐标;准备实体类
- 创建空的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
中包含set
和where
部分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();
}
}
功能测试
打开页面,重新验证部门管理的功能是否正常
觉得有用的话,点赞收藏就是对硬核干货最好的认可~谢谢啦~