一、为什么要用 MyBatis-Plus?
什么是 MyBatis-Plus?
官方定位 :MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
它提供了通用 Mapper 和通用 Service,可以在不编写任何 SQL 语句的前提下,快速实现单表的增删改查(CRUD)、批量、逻辑删除、分页等操作。
核心特性一览
flowchart LR
subgraph 传统开发
A1[手写 Entity] --> A2[手写 Mapper 接口]
A2 --> A3[手写 Mapper.xml SQL]
A3 --> A4[手写 Service]
end
subgraph MP开发
B1[实体类] --> B2[继承 BaseMapper]
B2 --> B3[自动注入全部CRUD<br/>无需写SQL]
B3 --> B4[继承 IService 获得<br/>通用业务方法]
end
A1 -.只做增强不做改变.-> B1
|---------------|-------------------------------------------|
| 特性 | 说明 |
| 无侵入 | 只做增强不做改变,引入它不会对现有工程产生影响 |
| 损耗小 | 启动即自动注入基本 CRUD,性能基本无损耗,直接面向对象操作 |
| 强大的 CRUD | 内置通用 Mapper、通用 Service,少量配置即可实现单表大部分 CRUD |
| 支持 Lambda | 通过 Lambda 表达式编写查询条件,不用担心字段写错 |
| 主键自动生成 | 支持多种主键策略(内含分布式唯一 ID 生成器) |
| 内置代码生成器 | 快速生成 Mapper、Model、Service、Controller 层代码 |
| 内置分页插件 | 基于 MyBatis 物理分页,写分页等同普通 List 查询 |
| 内置性能分析插件 | 可输出 SQL 及执行时间,快速揪出慢查询 |
| 内置全局拦截插件 | 提供全表 delete/update 智能分析阻断,预防误操作 |
支持哪些数据库?
任何能使用 MyBatis 进行 CRUD、并支持标准 SQL 的数据库都支持,包括:
MySQL、Oracle、DB2、H2、HSQL、SQLite、PostgreSQL、SQLServer、Phoenix、Gauss、ClickHouse、Sybase、OceanBase、达梦、人大金仓等。
二、开发环境准备
|--------------|---------------|
| 组件 | 版本 |
| IDE | IntelliJ IDEA |
| JDK | JDK 8+ |
| 构建工具 | Maven 3.5.4 |
| 数据库 | MySQL 5.7 |
| Spring Boot | 2.6.3 |
| MyBatis-Plus | 3.5.1 |
三、快速入门:跑通第一个查询
创建数据库及表
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 插入测试数据
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
初始化 Spring Boot 工程并引入依赖
<dependencies>
<!-- Spring Boot 基础 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- MyBatis-Plus 核心依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
配置 application.yml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
username: root
password: 123456
踩坑提醒:
- 驱动类:Spring Boot 2.0(内置 JDBC5)用 com.mysql.jdbc.Driver;2.1 及以上(内置 JDBC8)用 com.mysql.cj.jdbc.Driver,否则测试会有 WARN。
- URL:MySQL 8.0 需要加 serverTimezone=GMT%2B8,否则报 The server time zone value ... is unrecognized。
编写启动类、实体、Mapper
// 启动类:扫描 mapper 包
@SpringBootApplication
@MapperScan("com.qcby.mybatisplus.mapper")
public class MybatisplusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisplusApplication.class, args);
}
}
// 实体类:使用 Lombok 简化
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
// Mapper 接口:继承 BaseMapper<User>,即拥有全部 CRUD
public interface UserMapper extends BaseMapper<User> {
}
测试:一个方法查全部
@SpringBootTest
public class MybatisPlusTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelectList() {
// selectList() 根据条件构造器查询一个 list 集合,null 表示无条件,即查询所有
userMapper.selectList(null).forEach(System.out::println);
}
}
运行后即可打印出全部 5 条用户数据。注意:IDEA 可能提示 userMapper 找不到注入对象(类是动态创建的),程序其实能正常运行;为了避免报错提示,可在 Mapper 接口上添加 @Repository 注解。
开启 SQL 日志
mybatis-plus:
configuration:
# 控制台打印 MyBatis 执行的 SQL,便于调试
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
四、基本 CRUD:BaseMapper 全掌握
BaseMapper<T> 内置了全部单表 CRUD 方法,直接继承即可使用。核心方法如下:
|----|---------------------------------|-------------|
| 操作 | 方法 | 说明 |
| 插入 | int insert(T entity) | 插入一条记录 |
| 删除 | int deleteById(Serializable id) | 根据 ID 删除 |
| 删除 | int deleteBatchIds(...) | 根据 ID 批量删除 |
| 删除 | int deleteByMap(Map) | 根据 map 条件删除 |
| 删除 | int delete(Wrapper) | 根据条件构造器删除 |
| 修改 | int updateById(T entity) | 根据 ID 修改 |
| 修改 | int update(T, Wrapper) | 根据条件更新 |
| 查询 | T selectById(Serializable id) | 根据 ID 查询 |
| 查询 | List<T> selectBatchIds(...) | 根据多个 ID 查询 |
| 查询 | List<T> selectList(Wrapper) | 查询全部/按条件 |
| 查询 | Long selectCount(Wrapper) | 查询总记录数 |
| 查询 | List<Map> selectMaps(Wrapper) | 查询返回 Map |
| 分页 | IPage selectPage(page, wrapper) | 分页查询 |
插入
@Test
public void testInsert() {
User user = new User(null, "张三", 23, "zhangsan@qcby.com");
// 执行: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
int result = userMapper.insert(user);
System.out.println("受影响行数:" + result);
// 打印自动生成的 id: 1475754982694199298
System.out.println("id自动获取:" + user.getId());
}
知识点 :插入时 id 自动生成为 1475754982694199298 这类超长数字------这是 MyBatis-Plus 默认基于雪花算法生成的主键。
删除(三种方式)
// 方式一:根据 id 删除
int result = userMapper.deleteById(1475754982694199298L);
// 方式二:根据多个 id 批量删除
int result = userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));
// 方式三:根据 map 条件删除(map 的 key 是字段名,value 是值)
Map<String, Object> map = new HashMap<>();
map.put("age", 23);
map.put("name", "张三");
int result = userMapper.deleteByMap(map); // DELETE FROM user WHERE name = ? AND age = ?
修改
@Test
public void testUpdateById() {
User user = new User(4L, "admin", 22, null);
// 只更新非 null 字段: UPDATE user SET name=?, age=? WHERE id=?
int result = userMapper.updateById(user);
}
重要特性 :updateById 只更新实体中非 null 的字段。email 传 null 不会被更新。
查询(四种方式)
// 方式一:根据 id 查询
User user = userMapper.selectById(4L);
// 方式二:根据多个 id 查询
List<User> list = userMapper.selectBatchIds(Arrays.asList(4L, 5L));
// 方式三:根据 map 条件查询
Map<String, Object> map = new HashMap<>();
map.put("age", 22);
map.put("name", "admin");
List<User> list = userMapper.selectByMap(map);
// 方式四:查询所有(null 表示无条件)
List<User> list = userMapper.selectList(null);
通过观察 BaseMapper 的方法,大多方法都有 Wrapper 类型的形参------这就是条件构造器,可针对 SQL 设置不同条件;若无条件,传 null 即查询/删除/修改所有数据。
五、通用 Service:业务层封装
基础 Mapper 属于 DAO 层,业务层还有一个更常用的封装------IService / ServiceImpl。
创建 Service 接口和实现类
// 接口:继承 IService<User>,获得通用业务方法
public interface UserService extends IService<User> {
}
// 实现类:继承 ServiceImpl<UserMapper, User>,获得基础实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
测试:查询记录数 & 批量插入
@Autowired
private UserService userService;
@Test
public void testGetCount() {
long count = userService.count();
System.out.println("总记录数:" + count);
}
@Test
public void testSaveBatch() {
// SQL 长度有限制,海量数据无法用单条 SQL 插入,
// 因此 MP 将批量插入放在通用 Service 实现,而非通用 Mapper
List<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setName("szy" + i);
user.setAge(20 + i);
users.add(user);
}
userService.saveBatch(users); // 批量插入
}
命名规范:通用 Service CRUD 采用 get(查询单行)、remove(删除)、list(查询集合)、page(分页)前缀命名,与 Mapper 层区分避免混淆。若需要自定义通用方法,建议创建自己的 IBaseService 继承 MP 提供的基类。
六、常用注解深入讲解
经过前面的测试,我们没指定操作哪张表,只是 Mapper 继承 BaseMapper 时设置了泛型 User,MP 就自动操作了 user 表。**这说明 MP 操作的表由泛型(实体类型)决定,默认表名 = 实体类名。**但真实开发中会遇到各种"不一致",就要靠注解解决。
@TableName:实体类名 ≠ 表名
问题:把表 user 改名为 t_user 后,查询报错 Table 'mybatis_plus.user' doesn't exist。
解决方式一(注解):在实体类上标注对应表名
@TableName("t_user")
@Data
public class User { ... }
解决方式二(全局配置):开发中常遇到表都有固定前缀(如 t_),可统一配置,无需每个实体类标注
mybatis-plus:
global-config:
db-config:
# 配置操作表的默认前缀
table-prefix: t_
@TableId:主键不是 id,或需要指定主键策略
问题:若实体的主键字段叫 uid 而非 id,MP 不会自动识别,报 Field 'uid' doesn't have a default value。
解决方式:用 @TableId 标识主键
@Data
public class User {
@TableId
private Long uid;
...
}
value 属性:当实体主键属性是 id、但表主键字段是 uid 时:
@TableId("uid") // 或 @TableId(value = "uid")
private Long id;
type 属性(主键策略):
|----------------------|------------------------|
| 值 | 描述 |
| IdType.ASSIGN_ID(默认) | 基于雪花算法生成 id,与数据库是否自增无关 |
| IdType.AUTO | 使用数据库自增,需确保数据库设置了自增 |
配置全局主键策略:
mybatis-plus:
global-config:
db-config:
id-type: auto # 全局使用数据库自增
@TableField:属性名 ≠ 字段名
情况 1(驼峰 vs 下划线) :实体属性 userName、表字段 user_name。MP 默认自动将下划线转驼峰,无需处理。
情况 2(不满足映射规则):实体属性 name、表字段 username。需用注解指定字段名:
@Data
public class User {
@TableField("username")
private String name;
...
}
@TableLogic:逻辑删除
物理删除 vs 逻辑删除:
- 物理删除:真实删除数据,之后查不到;
- 逻辑删除 :假删除,把"是否删除"状态字段改为已删除,数据库仍保留记录,可数据恢复。
实现三步走:
- 数据库加逻辑删除列 is_deleted,默认值 0;
- 实体类加对应属性并标 @TableLogic;
-
测试:删除操作实际执行的是修改,查询会自动过滤已删除数据。
@Data
public class User {
...
@TableLogic
private Integer isDeleted;
}
执行效果(自动生成的 SQL):
删除时实际执行修改
UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0
查询时自动过滤已删除
SELECT ... FROM t_user WHERE is_deleted=0
七、总结
回顾本文,你已经掌握了 MyBatis-Plus 最核心的入门能力:
- 环境搭建:Spring Boot + MP 3.5.1 + Lombok + MySQL,跑通第一个查询;
- 基础 CRUD:BaseMapper 内置全部单表增删改查,updateById 只更新非 null 字段;
- 通用 Service:IService / ServiceImpl 封装业务层,支持 count、saveBatch 等;
- 常用注解:@TableName 解决表名、@TableId 解决主键与策略、@TableField 解决字段映射、@TableLogic 实现逻辑删除。