MyBatis-Plus超详细教程,从入门到精通
作为Java后端开发者,我们都知道MyBatis是一款优秀的持久层框架,但它的XML配置繁琐、单表CRUD代码重复等问题,往往会占用我们大量的开发时间。而MyBatis-Plus(简称MP)的出现,完美解决了这些痛点——它是MyBatis的增强工具,在MyBatis基础上只做增强不做改变,既能保留MyBatis的灵活性,又能极大简化开发、提高效率,成为目前国内后端开发中最流行的持久层框架之一。
本文将从MyBatis-Plus概述、快速入门、日志配置、常用注解、Mapper CRUD接口、条件构造器等核心模块,结合具体案例详细讲解,全程干货无冗余,无论是新手入门还是老手查漏补缺,都能有所收获。
一、MyBatis-Plus概述
1. 简介
MyBatis-Plus是由国内团队「苞米豆(baomidou)」开发的MyBatis增强工具,官网地址:https://baomidou.com/。
其核心定位是「增强不改变」,即引入MP后,不会对现有MyBatis工程产生任何侵入性影响,开发者依然可以使用MyBatis的原生语法,同时能享受MP提供的各种便捷功能,彻底摆脱重复的CRUD代码编写。
关于多表操作的解决方案,MP提供两种常用思路:
-
方式一:沿用MyBatis的原生方式,编写一对一、一对多、多对多的关联查询SQL;
-
方式二:在Service层通过多次单表条件查询,再通过代码逻辑组装关联数据(适合中小项目,简化SQL编写)。
2. 核心特性(必记)
MP的强大之处,在于它内置了大量实用功能,无需额外开发,开箱即用:
-
无侵入:只做增强不做改变,引入后不影响现有工程,无缝衔接MyBatis;
-
损耗小:启动时自动注入基本CRUD方法,性能几乎无损耗,直接面向对象操作;
-
强大的CRUD操作:内置通用Mapper、通用Service,少量配置即可实现单表大部分CRUD,搭配条件构造器满足各类查询需求;
-
Lambda形式调用:通过Lambda表达式编写查询条件,避免字段名写错的问题,代码更简洁;
-
主键自动生成:支持4种主键策略(含分布式唯一ID生成器Sequence),完美解决主键生成难题;
-
ActiveRecord模式:实体类继承Model类即可实现强大的CRUD操作,无需编写Mapper接口;
-
自定义全局通用操作:一次编写,全局复用,减少重复代码;
-
内置代码生成器:通过代码或Maven插件,快速生成Mapper、Model、Service、Controller层代码,支持模板自定义;
-
内置分页插件:基于MyBatis物理分页,配置简单,写分页等同于普通List查询,支持多种数据库;
-
性能分析插件:开发测试时可输出SQL语句及执行时间,快速定位慢查询;
-
全局拦截插件:智能阻断全表delete、update操作,可自定义拦截规则,预防误操作。
3. 支持的数据库
任何能使用MyBatis进行CRUD且支持标准SQL的数据库,MP都能支持,包括但不限于:
MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer、Phoenix、Gauss、ClickHouse、Sybase、OceanBase等;
同时支持国内主流数据库:达梦、虚谷、人大金仓、南大通用、神通、瀚高数据库等。
4. 框架结构与执行流程
核心结构
MyBatis-Plus的核心结构分为4部分,层层递进,支撑起整个框架的功能:
-
MyBatis-Plus场景启动器:负责整合Spring Boot与MP,简化配置;
-
注解组件:提供各类注解(如@TableName、@TableId),实现实体与数据库表的映射;
-
核心组件:封装CRUD、条件构造、分页等核心功能;
-
代码生成组件:快速生成各层代码,提升开发效率。
执行流程(核心)
MP的核心执行逻辑的是“自动生成SQL并注入MyBatis容器”,具体流程如下:
-
启动项目时,MP扫描指定包下的实体类;
-
通过Java反射机制,提取实体类的表名、字段名、字段类型等信息;
-
根据提取的信息,自动生成insert、update、delete、select等SQL语句;
-
将生成的SQL注入到MyBatis容器中,开发者直接调用方法即可执行。
二、MyBatis-Plus快速入门(实战案例)
本案例基于Spring Boot + Maven + MySQL,实现User表的CRUD操作,全程无XML配置,新手可直接跟着操作。
参考官网快速入门文档:https://baomidou.com/pages/24112f/
前置条件
在开始前,请确保你已具备以下环境和知识:
-
Java开发环境(JDK 8及以上)及IDE(IDEA推荐);
-
熟悉Spring Boot基础;
-
熟悉Maven依赖管理;
-
MySQL数据库(5.7及以上)。
实现步骤(共7步)
步骤1:创建数据库和表
首先创建数据库(本文命名为boot),然后创建user表并插入测试数据,SQL语句如下:
DROP TABLE IF EXISTS user;
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
DELETE FROM user;
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');
步骤2:创建Spring Boot项目
使用IDEA创建Spring Boot项目,初始化时选择Lombok、Spring Web依赖(后续手动添加MP和MySQL驱动依赖)。
步骤3:导入依赖(关键)
在pom.xml中添加MySQL驱动、Lombok、MyBatis-Plus启动器依赖,注意:尽量不要同时导入MyBatis和MyBatis-Plus,避免冲突。
<!-- 引入mysql的驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 引入lombok的依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 引入mybatis-plus的依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
步骤4:配置数据库连接
在application.properties文件中配置MySQL连接信息,替换自己的数据库用户名和密码:
# DataSource Config
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/boot?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
步骤5:编写实体类(POJO)
创建com.mp.pojo包,编写User实体类,使用Lombok的@Data注解简化getter/setter方法:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
步骤6:编写Mapper接口
创建com.mp.mapper包,编写UserMapper接口,只需继承BaseMapper<User>,即可获得MP内置的所有CRUD方法,无需编写任何SQL。
@Mapper // mybatis的注解,标识该接口为Mapper接口
public interface UserMapper extends BaseMapper<User> {
// 无需编写任何方法,BaseMapper已内置所有CRUD功能
}
步骤7:编写启动类并测试
在启动类上添加@MapperScan注解,指定Mapper接口的扫描路径(如果Mapper接口添加了@Mapper注解,也可省略@MapperScan):
@MapperScan("com.mp.mapper") // 扫描Mapper接口所在包
@SpringBootApplication
public class SmpApplication {
public static void main(String[] args) {
SpringApplication.run(SmpApplication.class, args);
}
}
测试CRUD(关键)
创建测试类(在test目录下),注入UserMapper,测试查询所有用户的功能:
@SpringBootTest
class SmpApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
// selectList(null) 表示无任何查询条件,查询所有数据
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
测试结果
运行测试方法,控制台会输出所有用户信息,说明MP整合成功:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
小结
通过以上7步,我们无需编写任何XML文件,也无需手动编写CRUD方法,就实现了User表的查询功能。这就是MP的强大之处——简化开发,让开发者专注于业务逻辑,而非重复的CRUD代码。
注意:如果实体类名与表名不一致,需在实体类上添加@TableName("表名")注解;如果实体类字段与表中列名不一致,需在字段上添加@TableField("列名")注解。
三、配置日志(查看SQL执行过程)
默认情况下,MP执行的SQL语句是不可见的,为了方便开发调试、查看SQL执行细节(如参数、执行时间),我们需要配置日志,让SQL语句打印到控制台。
配置方式(application.properties)
# 配置MyBatis-Plus日志(打印SQL到控制台)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
配置后效果
再次运行测试方法,控制台会输出SQL执行过程,包括SQL语句、参数、执行时间等信息,例如:
==> Preparing: SELECT id,name,age,email FROM user
==> Parameters:
<== Columns: id, name, age, email
<== Row: 1, Jone, 18, test1@baomidou.com
<== Row: 2, Jack, 20, test2@baomidou.com
<== Row: 3, Tom, 28, test3@baomidou.com
<== Row: 4, Sandy, 21, test4@baomidou.com
<== Row: 5, Billie, 24, test5@baomidou.com
<== Total: 5
通过日志,我们可以快速定位SQL问题,比如字段写错、条件错误等,极大提升调试效率。
四、MyBatis-Plus常用注解(必掌握)
MP提供了一系列注解,用于实现实体类与数据库表、字段的映射,解决命名不一致、主键策略等问题,以下是最常用的8个注解,重点掌握前4个。
1. @TableName(表名注解)
「作用」:标识实体类对应的数据库表名,当实体类名与表名不一致时必须使用。
「使用位置」:实体类类名上
// 示例:实体类名User,数据库表名sys_user
@TableName("sys_user")
public class User {
private Long id;
private String name;
// 其他字段...
}
「核心属性」:
-
value:表名(必填,当实体类名与表名一致时可省略);
-
keepGlobalPrefix:是否保持使用全局tablePrefix(若全局配置了表前缀,且自行设置了value,可控制是否拼接前缀);
-
autoResultMap:是否自动构建resultMap(默认false,复杂字段映射时可开启)。
2. @TableId(主键注解)
「作用」:标识实体类的主键字段,用于指定主键策略。
「使用位置」:实体类主键字段上
public class User {
// 主键字段,指定主键策略为数据库自增
@TableId(type = IdType.AUTO)
private Long id;
// 其他字段...
}
「核心属性」:
-
value:主键字段名(当实体类字段与表中主键列名一致时可省略);
-
type:主键策略(重点,默认IdType.NONE,跟随全局配置)。
「主键策略(IdType)详解」:
-
AUTO:数据库ID自增(需数据库表主键设置为自增);
-
NONE:未设置主键类型,跟随全局配置;
-
INPUT:插入前自行设置主键值(手动输入);
-
ASSIGN_ID:分配ID(支持Long、Integer、String类型),使用雪花算法(默认);
-
ASSIGN_UUID:分配UUID(主键类型必须为String);
-
ID_WORKER/UUID/ID_WORKER_STR:已过时,推荐使用ASSIGN_ID/ASSIGN_UUID。
3. @TableField(字段注解,非主键)
「作用」:标识实体类的非主键字段,解决实体字段与表中列名不一致、字段是否为数据库表字段等问题。
「使用位置」:实体类非主键字段上
public class User {
@TableId(type = IdType.AUTO)
private Long id;
// 实体字段name,数据库列名username
@TableField("username")
private String name;
// 其他字段...
}
「核心属性」(常用):
-
value:数据库字段名(字段名一致时可省略);
-
exist:是否为数据库表字段(默认true,若实体字段不是表中字段,需设为false);
-
fill:字段自动填充策略(如创建时间、修改时间,后续详解);
-
select:是否参与查询(默认true,若不想查询该字段,设为false);
-
update:更新时的注入逻辑(如update="%s+1",表示更新时自增1)。
4. @TableLogic(逻辑删除注解)
「作用」:实现逻辑删除(数据不真正删除,仅标记删除状态),避免误删除数据无法恢复。
「使用场景」:实际开发中,几乎所有表都需要逻辑删除,替代物理删除。
「使用步骤」:
-
数据库表添加deleted字段(int类型,默认值0,0=未删除,1=已删除);
-
实体类添加deleted字段,并添加@TableLogic注解;
-
配置文件中配置逻辑删除的默认值。
// 实体类字段
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
// 逻辑删除字段
@TableLogic
private Integer deleted;
// 其他字段...
}
# 配置逻辑删除(application.properties)
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 逻辑已删除值(默认1)
logic-not-delete-value: 0 # 逻辑未删除值(默认0)
「逻辑删除原理」:
-
删除操作:实际执行update语句,将deleted设为1(而非delete语句);
-
查询操作:自动追加where deleted=0条件,过滤已删除数据;
-
更新操作:自动追加where deleted=0条件,避免更新已删除数据。
5. 其他常用注解(了解即可)
-
@OrderBy:内置SQL默认排序,优先级低于条件构造器;
-
@Version:乐观锁注解,用于解决并发更新冲突;
-
@EnumValue:普通枚举类注解,用于枚举字段与数据库值的映射;
-
@KeySequence:序列主键策略(适用于Oracle数据库)。
五、Mapper CRUD接口(核心功能)
MP的BaseMapper接口内置了所有单表CRUD方法,无需手动编写SQL,直接调用即可。以下是最常用的方法,结合案例详细讲解。
「说明」:
-
泛型T为实体类对象;
-
参数Serializable为任意类型主键(推荐使用Long);
-
参数Wrapper为条件构造器(后续详解),null表示无查询条件。
1. 插入操作(insert)
「方法格式」:int insert(T entity); // 插入一条记录,返回受影响行数
「案例」:插入一条用户数据,主键自动生成(雪花算法)
@Test
public void testInsert() {
User user = new User();
user.setName("李思思");
user.setAge(18);
user.setEmail("379872721@qq.com");
// 插入数据,MP自动生成主键
int rows = userMapper.insert(user);
System.out.println("受影响行数:" + rows);
System.out.println("插入的用户ID:" + user.getId()); // 主键自动回填
}
「注意」:主键生成策略可通过@TableId(type = ...)局部设置,或通过全局配置设置(优先级:局部 > 全局)。
2. 更新操作(update)
MP提供两种更新方式,最常用的是根据ID更新和根据条件更新。
方式1:根据ID更新(updateById)
@Test
public void testUpdateById() {
User user = new User();
user.setId(6L); // 必须设置主键ID
user.setName("李思思_修改");
user.setAge(19);
// 根据ID更新,只更新非null字段
int rows = userMapper.updateById(user);
System.out.println("受影响行数:" + rows);
}
方式2:根据条件更新(update + Wrapper)
@Test
public void testUpdateByWrapper() {
// 1. 创建条件构造器,设置更新条件
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "李思思_修改"); // 条件:name等于李思思_修改
// 2. 创建更新对象,设置更新内容
User user = new User();
user.setAge(20);
// 3. 根据条件更新
int rows = userMapper.update(user, wrapper);
System.out.println("受影响行数:" + rows);
}
3. 查询操作(常用5种)
查询操作是开发中最常用的,MP提供了多种查询方式,覆盖大部分场景。
(1)根据ID查询(selectById)
@Test
public void testSelectById() {
User user = userMapper.selectById(1L);
System.out.println(user); // 输出查询到的用户信息
}
(2)根据ID批量查询(selectBatchIds)
@Test
public void testSelectBatchIds() {
// 批量查询ID为1、2、3的用户
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.forEach(System.out::println);
}
(3)根据多条件查询(selectByMap)
「说明」:Map中的key为数据库字段名,value为查询值,多条件为“且”关系。
@Test
public void testSelectByMap() {
Map<String, Object> map = new HashMap<>();
map.put("name", "Jack"); // 条件1:name=Jack
map.put("age", 20); // 条件2:age=20
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
(4)查询所有数据(selectList)
@Test
public void testSelectList() {
// selectList(null) 表示无任何条件,查询所有
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
(5)查询总记录数(selectCount)
@Test
public void testSelectCount() {
// 查询所有记录数(无条件)
Integer count = userMapper.selectCount(null);
System.out.println("总记录数:" + count);
// 带条件查询记录数(查询age>=18的用户数)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 18);
Integer count2 = userMapper.selectCount(wrapper);
System.out.println("age>=18的用户数:" + count2);
}
4. 分页查询(核心,必掌握)
分页是网站开发中必备功能,MP内置分页插件,无需额外引入第三方插件(如PageHelper),配置简单、使用便捷。
实现步骤(2步)
步骤1:配置分页拦截器
创建配置类(如MybatisPlusConfig),注册分页拦截器,指定数据库类型(本文为MySQL):
@Configuration
public class MybatisPlusConfig {
// 最新版分页插件配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页拦截器,指定数据库类型为MySQL
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(com.baomidou.mybatisplus.core.toolkit.supportDbType.MYSQL));
return interceptor;
}
}
步骤2:编写分页查询代码
@Test
public void testSelectPage() {
// 1. 创建分页对象,参数1:当前页码,参数2:每页显示条数
Page<User> page = new Page<>(2, 3); // 第2页,每页3条数据
// 2. 执行分页查询,null表示无查询条件
Page<User> userPage = userMapper.selectPage(page, null);
// 3. 获取分页结果
List<User> records = userPage.getRecords(); // 分页查询的数据列表
long total = userPage.getTotal(); // 总记录数
long current = userPage.getCurrent(); // 当前页码
long pages = userPage.getPages(); // 总页码
boolean hasNext = userPage.hasNext(); // 是否有下一页
boolean hasPrevious = userPage.hasPrevious(); // 是否有上一页
// 输出结果
System.out.println("分页数据:");
records.forEach(System.out::println);
System.out.println("总记录数:" + total);
System.out.println("当前页码:" + current);
System.out.println("总页码:" + pages);
}
5. 删除操作(物理删除 + 逻辑删除)
删除操作分为物理删除和逻辑删除,实际开发中优先使用逻辑删除。
(1)物理删除(直接删除数据)
// 1. 根据ID删除
@Test
public void testDeleteById() {
int rows = userMapper.deleteById(6L);
System.out.println("受影响行数:" + rows);
}
// 2. 根据ID批量删除
@Test
public void testDeleteBatchIds() {
int rows = userMapper.deleteBatchIds(Arrays.asList(6L, 7L));
System.out.println("受影响行数:" + rows);
}
// 3. 根据条件删除
@Test
public void testDeleteByMap() {
Map<String, Object> map = new HashMap<>();
map.put("name", "李思思");
map.put("age", 18);
int rows = userMapper.deleteByMap(map);
System.out.println("受影响行数:" + rows);
}
(2)逻辑删除(推荐)
配置逻辑删除后,调用delete系列方法,实际执行的是update操作(将deleted设为1),具体案例参考「@TableLogic注解」部分。
六、条件构造器(Wrapper,核心难点)
当我们需要编写复杂的SQL查询条件(如多条件、模糊查询、排序、子查询等)时,就需要使用MP的条件构造器Wrapper。
MP提供两种常用的条件构造器:
-
QueryWrapper:普通条件构造器,使用字符串指定字段名(易出错);
-
LambdaQueryWrapper:Lambda条件构造器,通过Lambda表达式指定字段名(推荐,避免字段名写错)。
参考官网条件构造器文档:https://baomidou.com/pages/10c804/#abstractwrapper
常用条件构造方法(实战案例)
以下案例均使用QueryWrapper,LambdaQueryWrapper用法类似,只需将“字段名字符串”替换为Lambda表达式(如user -> user.getName())。
案例1:多条件查询(非空 + 范围)
需求:查询姓名不为空、邮箱不为空、年龄大于等于12的用户。
@Test
void testWrapper1() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name") // 姓名不为空
.isNotNull("email") // 邮箱不为空
.ge("age", 12); // 年龄>=12
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
案例2:精确查询(等于)
需求:查询姓名为“Jack”的用户(注意:selectOne只能查询一条记录,多一条会报错)。
@Test
void testWrapper2() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "Jack"); // 姓名等于Jack
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
案例3:范围查询(between)
需求:查询年龄在20~30之间的用户数。
@Test
void testWrapper3() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30); // 年龄在20到30之间(包含20和30)
Integer count = userMapper.selectCount(wrapper);
System.out.println("年龄在20~30之间的用户数:" + count);
}
案例4:模糊查询(like)
需求:查询姓名不包含“e”、邮箱以“t”开头的用户。
@Test
void testWrapper4() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.notLike("name", "e") // 姓名不包含e(%e%)
.likeRight("email", "t"); // 邮箱以t开头(t%)
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
案例5:子查询(inSql)
需求:查询ID大于等于2的用户(子查询:select id from user where id>=2)。
@Test
void testWrapper5() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id", "select id from user where id >= 2"); // 子查询
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
常用条件构造方法总结
-
eq:等于(=);ne:不等于(!=);
-
gt:大于(>);ge:大于等于(>=);lt:小于(<);le:小于等于(<=);
-
like:模糊查询(%值%);likeLeft:左模糊(%值);likeRight:右模糊(值%);
-
between:范围查询(between 左 and 右);
-
in:包含查询(in (值1, 值2));inSql:子查询包含;
-
isNull:字段为空;isNotNull:字段不为空;
-
orderByAsc:升序排序;orderByDesc:降序排序。
七、总结
MyBatis-Plus作为MyBatis的增强工具,核心价值在于“简化开发、提高效率”,它没有改变MyBatis的原生语法,却解决了MyBatis的诸多痛点。
本文从概述、快速入门、日志配置、常用注解、Mapper CRUD、条件构造器等核心模块,详细讲解了MyBatis-Plus的使用方法,覆盖了开发中90%以上的场景。掌握这些内容,你可以轻松应对单表CRUD、分页、复杂查询等需求,摆脱重复的SQL编写工作。
后续还可以深入学习MP的高级功能,如代码生成器、全局通用方法注入、乐观锁、字段自动填充等,进一步提升开发效率。
最后,附上MyBatis-Plus官网地址,建议大家多查阅官网文档,获取最准确的功能说明:https://baomidou.com/
更多推荐




所有评论(0)