目录
一、MyBatis-Plus 核心优势
二、MyBatis-Plus 核心组件
1. 核心接口 / 类
2. 核心注解
三、Spring Boot 集成 MyBatis-Plus 实战
1. 环境准备
(1)依赖引入(Maven)
(2)核心配置(application.yml)
2. 代码实现
(1)数据库表设计
(2)实体类(Entity)
(3)自定义字段填充处理器(处理 createTime/updateTime)
(4)Mapper 接口(继承 BaseMapper)
(5)Service 层(通用 Service)
① Service 接口(继承 IService)
② Service 实现类(继承 ServiceImpl)
(6)启动类
3. 核心功能使用示例
(1)通用 CRUD(Mapper 层)
(2)条件构造器(QueryWrapper/LambdaQueryWrapper)
(3)自动分页查询
(4)通用 Service 层使用(批量操作、分页)
四、MyBatis-Plus 高级功能
1. 乐观锁插件(解决并发更新)
(1)添加乐观锁字段
(2)实体类添加注解
(3)配置乐观锁插件
(4)使用示例
2. 代码生成器(AutoGenerator)
(1)引入代码生成器依赖
(2)生成代码示例
3. 多数据源配置
(1)引入多数据源依赖
(2)配置多数据源(application.yml)
(3)使用多数据源(注解指定)
五、MyBatis-Plus 与 MyBatis 对比
六、最佳实践
七、总结
MyBatis-Plus(简称 MP)是基于 MyBatis 的增强工具,在 MyBatis 基础上只做增强不做改变,提供了大量开箱即用的功能(如通用 CRUD、分页、条件构造器等),彻底简化了 MyBatis 的开发,大幅减少重复的 SQL 编写工作。
一、MyBatis-Plus 核心优势
| 特性 | 说明 |
|---|---|
| 通用 CRUD | 内置 BaseMapper/IService,无需编写基础增删改查 SQL |
| 条件构造器 | 链式编程构建动态 SQL,替代 MyBatis 繁琐的 XML 动态标签 |
| 自动分页 | 内置分页插件,无需集成 PageHelper,分页查询更简单 |
| 代码生成器 | 一键生成 Entity、Mapper、Service、Controller 全套代码 |
| 主键生成策略 | 支持雪花算法、自增、UUID 等多种主键生成方式,无需手动配置 |
| 逻辑删除 | 自动处理逻辑删除(标记删除),无需手动写删除 SQL |
| 乐观锁 | 内置乐观锁插件,解决并发更新问题 |
| 多数据源 | 简单配置即可实现多数据源切换,无需复杂的代码改造 |
| 注解式开发 | 支持注解替代 XML,同时兼容 MyBatis 原有 XML 写法 |
二、MyBatis-Plus 核心组件
1. 核心接口 / 类
- BaseMapper<T>:通用 Mapper 接口,封装了单表 CRUD 操作,自定义 Mapper 继承即可使用。
- IService<T> / ServiceImpl<M,T>:通用 Service 层接口 / 实现,封装了批量操作、分页查询等高级 CRUD。
- QueryWrapper/LambdaQueryWrapper:条件构造器,用于动态构建 WHERE 条件(Lambda 版更优雅)。
- UpdateWrapper/LambdaUpdateWrapper:更新条件构造器,用于动态构建更新语句。
- Page<T>:分页对象,配合分页插件实现自动分页。
2. 核心注解
| 注解 | 作用 |
|---|---|
| @TableName | 映射数据库表名(实体类名与表名不一致时使用) |
| @TableId | 标记主键字段,指定主键生成策略(如 IdType.ASSIGN_ID 雪花算法) |
| @TableField | 映射数据库字段(字段名与属性名不一致、忽略字段、填充字段等) |
| @TableLogic | 标记逻辑删除字段(如 deleted=0 未删除,deleted=1 已删除) |
| @Version | 标记乐观锁字段(用于并发更新控制) |
| @TableFill | 标记自动填充字段(如创建时间、更新时间) |
三、Spring Boot 集成 MyBatis-Plus 实战
1. 环境准备
(1)依赖引入(Maven)
xml
<!-- Spring Boot 父工程 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</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>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- Lombok(简化实体类) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- Spring Boot 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>(2)核心配置(application.yml)
yaml
# 数据源配置 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mp_demo?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8 username: root password: 123456 # 连接池配置(默认 HikariCP) hikari: maximum-pool-size: 10 minimum-idle: 5 # MyBatis-Plus 配置 mybatis-plus: # 实体类别名包 type-aliases-package: com.example.mp.entity # Mapper XML 文件位置(如需保留 XML 写法) mapper-locations: classpath:mapper/**/*.xml # 全局配置 global-config: db-config: # 主键生成策略:雪花算法(分布式唯一ID) id-type: assign_id # 逻辑删除配置 logic-delete-field: deleted # 逻辑删除字段名 logic-delete-value: 1 # 已删除值 logic-not-delete-value: 0 # 未删除值 # 配置项(兼容 MyBatis 配置) configuration: map-underscore-to-camel-case: true # 自动下划线转驼峰(默认开启) log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志2. 代码实现
(1)数据库表设计
sql
CREATE DATABASE IF NOT EXISTS mp_demo; USE mp_demo; -- 用户表(含逻辑删除、自动填充字段) CREATE TABLE `user` ( `id` BIGINT NOT NULL COMMENT '主键ID(雪花算法)', `user_name` VARCHAR(50) NOT NULL COMMENT '用户名', `age` INT COMMENT '年龄', `email` VARCHAR(100) COMMENT '邮箱', `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除(0-未删,1-已删)', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) COMMENT '用户表';(2)实体类(Entity)
java
运行
package com.example.mp.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; @Data @TableName("user") // 表名映射(实体名与表名一致时可省略) public class User { // 主键:雪花算法生成(IdType.ASSIGN_ID) @TableId(type = IdType.ASSIGN_ID) private Long id; // 字段映射(属性名与字段名一致时可省略) @TableField("user_name") private String userName; private Integer age; private String email; // 逻辑删除字段 @TableLogic private Integer deleted; // 自动填充字段:创建时间 @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; // 自动填充字段:更新时间 @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }(3)自定义字段填充处理器(处理 createTime/updateTime)
java
运行
package com.example.mp.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component // 必须交给 Spring 管理 public class MyMetaObjectHandler implements MetaObjectHandler { // 插入时填充 @Override public void insertFill(MetaObject metaObject) { // 填充 createTime 和 updateTime this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } // 更新时填充 @Override public void updateFill(MetaObject metaObject) { // 只填充 updateTime this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }(4)Mapper 接口(继承 BaseMapper)
java
运行
package com.example.mp.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.entity.User; import org.apache.ibatis.annotations.Mapper; // 无需编写任何方法,BaseMapper 已封装所有基础 CRUD @Mapper // 或在启动类加 @MapperScan("com.example.mp.mapper") public interface UserMapper extends BaseMapper<User> { // 如需自定义 SQL,可在此编写(兼容 MyBatis 注解/XML 方式) // 示例:自定义查询 // @Select("SELECT * FROM user WHERE user_name = #{userName}") // List<User> selectByUserName(String userName); }(5)Service 层(通用 Service)
① Service 接口(继承 IService)
java
运行
package com.example.mp.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.mp.entity.User; public interface UserService extends IService<User> { // 如需自定义业务方法,在此扩展 }② Service 实现类(继承 ServiceImpl)
java
运行
package com.example.mp.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.mp.entity.User; import com.example.mp.mapper.UserMapper; import com.example.mp.service.UserService; import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 通用 Service 方法已全部实现,无需编写代码 }(6)启动类
java
运行
package com.example.mp; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // 批量扫描 Mapper 接口(替代每个 Mapper 加 @Mapper) @MapperScan("com.example.mp.mapper") @SpringBootApplication public class MpDemoApplication { public static void main(String[] args) { SpringApplication.run(MpDemoApplication.class, args); } }3. 核心功能使用示例
(1)通用 CRUD(Mapper 层)
java
运行
package com.example.mp; import com.example.mp.entity.User; import com.example.mp.mapper.UserMapper; import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest public class MapperCrudTest { @Resource private UserMapper userMapper; // 新增 @Test public void testInsert() { User user = new User(); user.setUserName("张三"); user.setAge(25); user.setEmail("zhangsan@example.com"); // insert 方法返回受影响行数 int rows = userMapper.insert(user); System.out.println("新增行数:" + rows + ",用户ID:" + user.getId()); // 雪花算法生成ID } // 根据ID查询 @Test public void testSelectById() { User user = userMapper.selectById(176928745896704L); System.out.println("查询结果:" + user); } // 查询所有 @Test public void testSelectList() { List<User> userList = userMapper.selectList(null); // null 表示无条件 userList.forEach(System.out::println); } // 根据ID更新 @Test public void testUpdateById() { User user = new User(); user.setId(176928745896704L); user.setAge(26); // 只更新age字段 int rows = userMapper.updateById(user); System.out.println("更新行数:" + rows); } // 根据ID删除(逻辑删除) @Test public void testDeleteById() { int rows = userMapper.deleteById(176928745896704L); System.out.println("删除行数:" + rows); // 实际执行 UPDATE user SET deleted=1 WHERE id=? } }(2)条件构造器(QueryWrapper/LambdaQueryWrapper)
java
运行
@Test public void testQueryWrapper() { // 1. 普通 QueryWrapper(需手动写字段名,易出错) QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("age", 25) // 等于 .like("user_name", "张") // 模糊查询 .orderByDesc("create_time"); // 排序 List<User> userList = userMapper.selectList(queryWrapper); System.out.println("普通条件查询:" + userList); // 2. LambdaQueryWrapper(类型安全,推荐) LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>(); lambdaQuery.eq(User::getAge, 25) .like(User::getUserName, "张") .orderByDesc(User::getCreateTime); List<User> lambdaList = userMapper.selectList(lambdaQuery); System.out.println("Lambda条件查询:" + lambdaList); }(3)自动分页查询
java
运行
@Test public void testPage() { // 1. 构建分页对象(第1页,每页10条) Page<User> page = new Page<>(1, 10); // 2. 分页查询(条件为null表示无条件) Page<User> resultPage = userMapper.selectPage(page, null); // 3. 分页结果解析 System.out.println("总条数:" + resultPage.getTotal()); System.out.println("总页数:" + resultPage.getPages()); System.out.println("当前页数据:" + resultPage.getRecords()); System.out.println("当前页码:" + resultPage.getCurrent()); System.out.println("每页条数:" + resultPage.getSize()); } // 自定义SQL分页(如需自定义查询) // 1. Mapper 接口添加方法 // Page<User> selectUserPage(Page<User> page, @Param("ew") LambdaQueryWrapper<User> queryWrapper); // 2. XML 编写 SQL(无需写 LIMIT,MP 自动拼接) // <select id="selectUserPage" resultType="User"> // SELECT * FROM user ${ew.customSqlSegment} // </select> // 3. 调用 // Page<User> page = new Page<>(1, 10); // LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().gt(User::getAge, 20); // Page<User> result = userMapper.selectUserPage(page, query);(4)通用 Service 层使用(批量操作、分页)
java
运行
package com.example.mp; 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 jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.util.Arrays; import java.util.List; @SpringBootTest public class ServiceCrudTest { @Resource private UserService userService; // 批量新增 @Test public void testSaveBatch() { User user1 = new User(); user1.setUserName("李四"); user1.setAge(24); User user2 = new User(); user2.setUserName("王五"); user2.setAge(26); boolean success = userService.saveBatch(Arrays.asList(user1, user2)); System.out.println("批量新增是否成功:" + success); } // 分页查询(Service 层) @Test public void testServicePage() { Page<User> page = new Page<>(1, 10); LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().gt(User::getAge, 20); IPage<User> resultPage = userService.page(page, query); System.out.println("Service分页总条数:" + resultPage.getTotal()); } // 批量删除 @Test public void testRemoveBatchByIds() { boolean success = userService.removeBatchByIds(Arrays.asList(176928745896704L, 176928745896705L)); System.out.println("批量删除是否成功:" + success); } // 条件查询单个结果 @Test public void testGetOne() { LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>().eq(User::getUserName, "张三"); User user = userService.getOne(query, false); // false 表示不抛出多条结果异常 System.out.println("单个结果:" + user); } }四、MyBatis-Plus 高级功能
1. 乐观锁插件(解决并发更新)
(1)添加乐观锁字段
sql
ALTER TABLE `user` ADD COLUMN `version` INT DEFAULT 1 COMMENT '乐观锁版本号';(2)实体类添加注解
java
运行
@Version // 标记乐观锁字段 private Integer version;(3)配置乐观锁插件
java
运行
package com.example.mp.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 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 PaginationInnerInterceptor()); // 乐观锁插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }(4)使用示例
java
运行
@Test public void testOptimisticLocker() { // 1. 查询用户(获取当前版本号) User user = userMapper.selectById(176928745896704L); // 2. 更新用户 user.setAge(27); int rows = userMapper.updateById(user); // 并发场景下,若版本号不匹配,rows=0(更新失败) System.out.println("更新行数:" + rows); }2. 代码生成器(AutoGenerator)
MP 提供了强大的代码生成器,可一键生成全套代码,减少重复劳动。
(1)引入代码生成器依赖
xml
<!-- MyBatis-Plus 代码生成器 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.5</version> </dependency> <!-- 模板引擎(默认 Velocity) --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>(2)生成代码示例
java
运行
package com.example.mp; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import java.util.Collections; public class CodeGenerator { public static void main(String[] args) { // 数据源配置 String url = "jdbc:mysql://localhost:3306/mp_demo?useSSL=false&serverTimezone=Asia/Shanghai"; String username = "root"; String password = "123456"; // 快速生成代码 FastAutoGenerator.create(url, username, password) // 全局配置 .globalConfig(builder -> { builder.author("mp-demo") // 设置作者 .outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录 .enableSwagger() // 开启 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_", "sys_") // 过滤表前缀(如 t_user → User) // 实体类策略 .entityBuilder() .enableLombok() // 开启 Lombok .enableTableFieldAnnotation() // 生成字段注解 .idType(com.baomidou.mybatisplus.annotation.IdType.ASSIGN_ID) // 主键策略 // Mapper 策略 .mapperBuilder() .enableBaseResultMap() // 生成 BaseResultMap .enableBaseColumnList() // 生成 BaseColumnList // Service 策略 .serviceBuilder() .formatServiceFileName("%sService") // Service 命名格式 .formatServiceImplFileName("%sServiceImpl") // ServiceImpl 命名格式 // Controller 策略 .controllerBuilder() .enableRestStyle(); // 开启 RestController 风格 }) // 执行生成 .execute(); } }3. 多数据源配置
MP 支持简单配置实现多数据源,适用于读写分离、分库等场景。
(1)引入多数据源依赖
xml
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.6.1</version> </dependency>(2)配置多数据源(application.yml)
yaml
spring: datasource: dynamic: primary: master # 默认数据源 strict: false # 关闭严格模式 datasource: # 主库 master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mp_demo_master?useSSL=false username: root password: 123456 # 从库 slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mp_demo_slave?useSSL=false username: root password: 123456(3)使用多数据源(注解指定)
java
运行
// Service 层指定数据源 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 主库(默认) public List<User> selectMaster() { return baseMapper.selectList(null); } // 从库 @DS("slave") // 指定从库数据源 public List<User> selectSlave() { return baseMapper.selectList(null); } }五、MyBatis-Plus 与 MyBatis 对比
| 维度 | MyBatis | MyBatis-Plus |
|---|---|---|
| 基础 CRUD | 需手动编写 XML / 注解 SQL | 继承 BaseMapper/IService 直接用 |
| 动态 SQL | XML 标签(if/where/foreach) | 条件构造器(链式编程) |
| 分页 | 需集成 PageHelper | 内置分页插件,一键分页 |
| 主键生成 | 需手动配置 | 内置多种策略(雪花算法 / 自增等) |
| 逻辑删除 | 需手动写 SQL | 注解 + 自动处理 |
| 代码生成 | 无原生支持 | 一键生成全套代码 |
| 兼容性 | 原生 MyBatis 语法 | 完全兼容 MyBatis,可混用 XML / 注解 |
六、最佳实践
- 优先使用 Lambda 条件构造器:避免硬编码字段名,减少拼写错误。
- 通用 CRUD 用 MP,复杂 SQL 用 XML:简单操作依赖 MP 通用方法,复杂关联查询仍用 MyBatis XML 更清晰。
- 合理使用逻辑删除:避免物理删除数据,便于数据恢复和审计。
- 乐观锁用于并发更新:如库存扣减、订单状态更新等场景。
- 代码生成器提升效率:新项目初始化时,用代码生成器一键生成基础代码。
- 分页查询必用 Page 对象:避免手动拼接 LIMIT,MP 自动适配不同数据库方言。
- 字段自动填充统一处理:创建时间、更新时间等字段通过填充处理器统一管理,避免重复代码。
七、总结
MyBatis-Plus 是 MyBatis 的 “黄金搭档”,在保留 MyBatis 灵活性的同时,大幅降低了开发成本:
- 核心价值:通用 CRUD 省去 80% 重复 SQL 编写,条件构造器简化动态 SQL,内置功能覆盖大部分业务场景。
- Spring Boot 集成:仅需引入依赖 + 简单配置,即可快速使用所有增强功能,完全兼容 MyBatis 原有代码。
- 适用场景:中小项目可完全基于 MP 开发,大型项目可混合使用 MP 通用方法 + MyBatis 复杂 SQL,兼顾效率与灵活性。
掌握 MyBatis-Plus 的关键是理解通用 CRUD 接口和条件构造器,在此基础上结合分页、乐观锁、代码生成器等高级功能,可极大提升开发效率。