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 提供 IService 和 ServiceImpl 封装批量操作、分页等高级功能:
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. 乐观锁插件
用于解决并发更新问题,步骤:
- 实体类添加
@Version注解(字段类型推荐 Integer/Long); - 配置乐观锁拦截器:
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;
}
}
- 使用示例:
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)自动填充,步骤:
- 实体类字段添加
@TableField(fill = FieldFill.INSERT/INSERT_UPDATE); - 实现填充处理器:
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、分页、逻辑删除等功能,减少重复代码。核心要点:
- 基础使用:继承
BaseMapper/IService快速实现 CRUD; - 高级特性:分页、乐观锁、自动填充、逻辑删除提升开发效率;
- 自定义扩展:通过 Wrapper、自定义 SQL 满足复杂业务需求;
- 工程化:代码生成器、多数据源适配企业级开发。