(三)从“重复CRUD累到炸”到“MyBatis-Plus一键控库”——MyBatis-Plus实战进阶指南
引言:掌握MyBatis后,我还是被CRUD搞崩溃了
自从跟着李哥学会MyBatis,我终于摆脱了“散装SQL”的噩梦——SQL统一写在XML里,代码与SQL解耦,排查问题、修改逻辑都轻松了不少。我一度以为,数据库操作已经达到了“优雅”的顶峰,直到接手公司的新项目,才发现自己还是太天真。
新项目是一个基础管理系统,包含用户、角色、菜单、订单等十几个基础模块,而每个模块都离不开“新增、查询、修改、删除”这一套CRUD操作。按照MyBatis的用法,我需要为每个实体类编写对应的Mapper接口,再在XML文件里编写重复的SQL语句:查询所有用select * from 表名,根据ID查询用select * from 表名 where id = #{id},新增用insert into 表名(字段1,字段2) values(#{字段1},#{字段2})……
仅仅是编写5个模块的CRUD,我就写了近百行重复的SQL和接口方法,手指敲得发麻不说,还频繁出现“字段漏写”“SQL语法错误”的问题。更麻烦的是,当实体类新增一个字段时,我不仅要修改实体类,还要同步修改XML里所有相关的SQL语句,稍有遗漏就会报错。
熬了两个晚上写完基础CRUD后,我顶着黑眼圈向李哥吐槽:“MyBatis虽然解耦了SQL和代码,但重复的CRUD操作也太繁琐了,感觉自己像个只会复制粘贴的工具人。”
李哥笑着递给我一杯咖啡:“这就是你没掌握MyBatis-Plus的原因。MyBatis-Plus是MyBatis的增强工具,它在MyBatis的基础上做了封装,不用你写一行SQL,就能实现大部分CRUD操作,还能轻松搞定分页、条件查询、逻辑删除这些常用功能,让你从重复的CRUD中解放出来,把精力放在核心业务上。”
那天之后,我抱着“摆脱工具人命运”的决心,从零开始学习MyBatis-Plus,从基础配置、一键CRUD,到条件构造、分页插件,再到高级封装、性能优化,一步步解锁了MyBatis-Plus的强大功能。今天,就把这段从“被CRUD折磨”到“一键控库”的成长历程,分享给和曾经的我一样,被重复CRUD困扰的Java新手开发者。
注:本文聚焦MyBatis-Plus 3.5.15(当前最新稳定版本,参考官方最新发布),结合Spring Boot 3.2.x整合实战,全程以“新手小王视角”展开,融入真实开发场景、踩坑经验和解决方案,所有代码块可直接复制使用,兼顾实用性和易懂性,让你从入门到精通,轻松掌握MyBatis-Plus。
第一章:新手破局——MyBatis-Plus是什么?为什么一定要学?
在学习MyBatis-Plus之前,我一直有个疑问:既然已经有了MyBatis,为什么还要学MyBatis-Plus?李哥用一个生动的类比,彻底帮我理清了思路:“如果说MyBatis是‘手动挡汽车’,需要你手动操作离合、油门,全程掌控;那MyBatis-Plus就是‘自动挡汽车’,它帮你简化了所有繁琐的操作,你只需要专注于‘驾驶’(核心业务),不用再纠结于‘换挡’(重复CRUD)。”
简单来说,MyBatis-Plus(简称MP)是baomidou团队开发的MyBatis增强工具,完全兼容MyBatis,不需要你放弃已有的MyBatis知识,就能快速上手。它的核心优势就是“简化开发”,把开发者从重复的CRUD代码中解放出来,同时提供了很多实用的增强功能,让数据库操作更高效、更优雅。据悉,MyBatis-Plus已连续5年获得开源中国年度最佳开源项目殊荣,Github累计16K Star,深受开发者认可。
一、MyBatis-Plus的核心优势(新手必懂)
对比MyBatis,MyBatis-Plus的优势非常明显,尤其是对于新手来说,能节省大量开发时间:
| 优势 | 具体说明 | 新手获益 |
|---|---|---|
| 无SQL一键CRUD | 内置BaseMapper接口,继承后即可获得新增、删除、修改、查询等基础CRUD方法,无需编写任何SQL | 不用写重复SQL,减少报错 |
| 条件构造器 | 提供QueryWrapper、LambdaQueryWrapper等,支持链式调用,灵活构建复杂查询条件,无需手动拼接SQL | 复杂查询更简单,避免SQL注入 |
| 自动分页 | 内置分页插件,配置后即可实现分页查询,无需手动编写limit语句,支持多种数据库 | 分页功能一键实现,无需手动处理 |
| 内置常用功能 | 支持逻辑删除、自动填充、乐观锁、主键生成策略等,无需手动编写代码实现 | 减少重复编码,提升开发效率 |
| 兼容MyBatis | 可以无缝整合MyBatis,已有MyBatis项目可直接迁移,支持XML编写复杂SQL | 无需重新学习,快速上手 |
二、环境准备(Spring Boot整合MyBatis-Plus,新手必看)
学习MyBatis-Plus的第一步,就是搭建整合环境。这里以Spring Boot 3.2.5(当前主流稳定版本) + MyBatis-Plus 3.5.15(官方最新版本) + MySQL 8.0为例,全程手把手教学,新手可直接复制配置,避免踩坑。
1. 导入依赖(pom.xml)
Spring Boot 3.2.x依赖Java 17及以上版本,需先确认本地JDK版本为17+;在pom.xml中,导入MyBatis-Plus的核心依赖、MySQL驱动依赖,无需额外导入MyBatis依赖(MyBatis-Plus已包含):
<!-- Spring Boot 3.2.x 父依赖(已引入可省略) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<!-- 指定JDK版本为17(Spring Boot 3.2.x 要求) -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- MyBatis-Plus 核心依赖(官方最新3.5.15版本) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.15</version>
</dependency>
<!-- MySQL 驱动(MySQL 8.0 对应此版本,适配Spring Boot 3.x) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
<scope>runtime</scope>
</dependency>
<!-- lombok(可选,简化实体类编写,不用写get/set方法) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot 测试依赖(用于测试CRUD方法) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. 配置application.yml(核心配置)
在resources目录下,编写application.yml文件,配置数据库连接信息、MyBatis-Plus相关配置(核心是 mapper-locations,指定mapper接口的位置),适配Spring Boot 3.x语法:
spring:
# 数据库配置(适配Spring Boot 3.x,驱动类已简化)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root # 你的MySQL用户名
password: 123456 # 你的MySQL密码
MyBatis-Plus 配置
mybatis-plus:
# 指定mapper接口的xml文件位置(如果不写XML,可省略此配置)
mapper-locations: classpath:mapper/**/*.xml
# 实体类扫描包(自动识别实体类,简化配置)
type-aliases-package: com.example.demo.entity
# 配置日志(可选,打印SQL语句,方便调试,新手推荐开启)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名自动映射(表中下划线字段对应实体类驼峰字段,默认开启,可显式配置)
map-underscore-to-camel-case: true
3. 启动类添加注解
在Spring Boot启动类上,添加@MapperScan注解,指定Mapper接口的扫描路径,让Spring能够识别Mapper接口(Spring Boot 3.x语法与2.x一致,无需修改):
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 扫描Mapper接口所在的包(替换成你自己的Mapper包路径)
@MapperScan("com.example.demo.mapper")
@SpringBootApplication
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
}
4. 测试环境是否搭建成功
环境搭建完成后,启动项目(需确保JDK17+、MySQL服务正常运行),如果控制台没有报错,且能看到“Connected to the target VM, address: ‘127.0.0.1:xxxx’, transport: ‘socket’”的提示,同时打印MyBatis-Plus相关启动日志,说明环境搭建成功,可以开始学习MyBatis-Plus的核心用法了。
三、新手入门:一键CRUD,告别重复SQL(核心中的核心)
MyBatis-Plus的最核心功能,就是“无SQL一键CRUD”。通过继承BaseMapper接口,我们不需要编写任何SQL语句,就能实现新增、删除、修改、查询等基础操作。下面以“用户管理”为例,李哥手把手教我实现一键CRUD,新手可直接复制代码测试。
1. 编写实体类(Entity)
创建User实体类,对应数据库中的t_user表(数据库表需提前创建,SQL语句附在下方),使用lombok简化get/set方法(如果不使用lombok,需手动编写get/set、toString等方法),适配Spring Boot 3.x的时间类型:
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
// @Data:lombok注解,自动生成get/set、toString、equals等方法
@Data
// @TableName:指定实体类对应的数据表名(如果实体类名和表名一致,可省略)
@TableName("t_user")
public class User {
// @TableId:指定主键字段,type=IdType.AUTO表示主键自增(MySQL常用)
@TableId(type = IdType.AUTO)
private Long id;
// 用户名(对应表中的username字段,字段名一致可省略@TableField)
private String username;
// 密码(对应表中的password字段)
private String password;
// 年龄(对应表中的age字段)
private Integer age;
// 邮箱(对应表中的email字段)
private String email;
// 创建时间(对应表中的create_time字段,下划线命名对应驼峰命名)
private LocalDateTime createTime;
}
2. 创建数据库表(t_user)
在MySQL中执行以下SQL,创建t_user表,字段与实体类对应,适配MySQL 8.0:
CREATE TABLE `t_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`age` int DEFAULT NULL COMMENT '年龄',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
3. 编写Mapper接口
创建UserMapper接口,继承MyBatis-Plus提供的BaseMapper接口,无需编写任何方法,即可获得所有基础CRUD功能(与Spring Boot 2.x用法一致,无缝兼容):
package com.example.demo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.User;
import org.springframework.stereotype.Repository;
// @Repository:标识为持久层组件,让Spring识别
@Repository
public interface UserMapper extends BaseMapper<User> {
// 无需编写任何方法,BaseMapper已提供所有基础CRUD方法
}
4. 编写Service层(可选,推荐)
在实际开发中,我们通常会在Service层封装业务逻辑,李哥告诉我,MyBatis-Plus提供了IService接口和ServiceImpl实现类,进一步简化Service层的开发,Spring Boot 3.x中用法不变。
(1)编写Service接口,继承IService:
package com.example.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.entity.User;
public interface UserService extends IService<User> {
// 可添加自定义业务方法,基础CRUD方法IService已提供
}
(2)编写Service实现类,继承ServiceImpl,实现UserService:
package com.example.demo.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 无需编写基础CRUD方法,ServiceImpl已实现,可直接调用
}
5. 测试一键CRUD(核心测试)
我们通过Spring Boot的测试类,测试BaseMapper和IService提供的基础CRUD方法,所有方法无需编写SQL,李哥帮我整理了完整的测试案例,新手可直接复制到测试类中执行:
package com.example.demo;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
// Spring Boot 3.x 测试注解(与2.x的@SpringBootTest一致)
@SpringBootTest
public class UserCrudTest {
// 注入UserService
@Autowired
private UserService userService;
// 测试新增用户
@Test
public void testAddUser() {
User user = new User();
user.setUsername("xiaowang");
user.setPassword("123456");
user.setAge(22);
user.setEmail("xiaowang@example.com");
user.setCreateTime(LocalDateTime.now());
// 调用IService的save方法,新增用户
boolean save = userService.save(user);
System.out.println("新增结果:" + (save ? "成功" : "失败"));
}
// 测试根据ID查询用户
@Test
public void testSelectById() {
// 调用IService的getById方法,根据ID查询
User user = userService.getById(1L);
System.out.println("查询到的用户:" + user);
}
// 测试查询所有用户
@Test
public void testSelectAll() {
// 调用IService的list方法,查询所有
List<User> userList = userService.list();
userList.forEach(user -> System.out.println("用户:" + user));
}
// 测试修改用户
@Test
public void testUpdateUser() {
User user = new User();
user.setId(1L); // 必须指定主键ID
user.setAge(23); // 修改年龄
user.setEmail("xiaowang_update@example.com"); // 修改邮箱
// 调用IService的updateById方法,根据ID修改
boolean update = userService.updateById(user);
System.out.println("修改结果:" + (update ? "成功" : "失败"));
}
// 测试根据ID删除用户
@Test
public void testDeleteById() {
// 调用IService的removeById方法,根据ID删除
boolean remove = userService.removeById(1L);
System.out.println("删除结果:" + (remove ? "成功" : "失败"));
}
}
6. 测试结果说明
执行测试方法后,控制台会打印对应的SQL语句(因为我们开启了日志配置),无需手动编写任何SQL,即可完成所有CRUD操作。李哥提醒我:测试时要注意,先执行新增方法,再执行查询、修改、删除方法,避免因ID不存在导致报错。
第二章:实战进阶——解决MyBatis-Plus新手常踩的“坑”
掌握了一键CRUD后,我以为自己可以顺利上手项目开发,结果刚一开始,就遇到了各种“坑”——主键自增失败、字段映射错误、分页失效……这些问题,也是新手最常遇到的,李哥陪着我逐一排查、解决,每解决一个,我对MyBatis-Plus的理解就深一分。
坑1:主键自增失败,新增用户时ID为null
场景:
执行新增用户测试时,控制台没有报错,但数据库中新增的用户ID为null,或者不是自增的连续数字,导致后续查询、修改、删除方法无法正常使用。
原因分析(李哥拆解):
-
实体类中@TableId注解的type属性配置错误,比如设置为IdType.NONE(无主键策略),而不是IdType.AUTO(主键自增);
-
数据库表中主键字段没有设置“自增”属性(比如忘记添加AUTO_INCREMENT);
-
MyBatis-Plus版本与Spring Boot 3.x不兼容(比如使用旧版本的MyBatis-Plus,无法适配JDK17)。
解决方法:
- 确认实体类@TableId注解配置正确,MySQL数据库优先使用IdType.AUTO:
// 正确配置(MySQL主键自增)
@TableId(type = IdType.AUTO)
private Long id;
-
确认数据库表主键字段设置了自增(参考前面的t_user表SQL,id字段添加了AUTO_INCREMENT);
-
确保MyBatis-Plus版本为3.5.15(官方最新版本),与Spring Boot 3.2.x兼容(参考前面的pom.xml依赖配置)。
坑2:字段映射失败,查询结果为null
场景:
执行查询方法时,控制台打印的SQL语句正确,数据库中也有对应的数据,但查询返回的实体类中,部分字段(比如createTime)为null,其他字段正常。
原因分析(李哥拆解):
-
实体类字段名与数据库表字段名不一致,且没有开启驼峰命名映射;
-
数据库表字段名是下划线命名(比如create_time),实体类字段名是驼峰命名(createTime),但MyBatis-Plus的驼峰映射功能未开启;
-
实体类中忘记添加@TableName注解,导致MyBatis-Plus无法识别实体类与数据表的对应关系。
解决方法:
- 开启驼峰命名自动映射(在application.yml中配置,前面已配置,可再次确认):
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 开启驼峰映射,默认开启,显式配置更稳妥
- 如果字段名差异较大(比如数据库是user_name,实体类是userName),可使用@TableField注解指定映射关系:
// 数据库字段是user_name,实体类字段是userName,手动指定映射
@TableField("user_name")
private String userName;
- 确认实体类添加了@TableName注解,指定对应的数据表名:
@TableName("t_user") // 必须指定数据表名,避免映射失败
@Data
public class User {
// 字段信息...
}
坑3:分页失效,查询结果返回所有数据
场景:
配置分页插件后,执行分页查询方法,控制台打印的SQL语句没有limit关键字,返回的是所有数据,分页功能失效。
原因分析(李哥拆解):
MyBatis-Plus的分页功能需要手动配置分页插件,Spring Boot 3.x中,分页插件的配置方式与2.x略有不同,新手容易沿用旧配置,导致分页失效。
解决方法:
在Spring Boot 3.2.x中,编写分页插件配置类,使用MyBatis-Plus 3.5.x的最新配置方式,李哥帮我整理了可直接复制的配置类:
package com.example.demo.config;
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 {
// 配置分页插件(Spring Boot 3.x 推荐配置方式)
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件,指定数据库类型(MySQL)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
配置完成后,重启项目,执行分页查询测试,李哥教我编写了分页查询的测试方法:
// 测试分页查询(在UserCrudTest类中添加)
@Test
public void testPage() {
// 分页参数:当前页(从1开始)、每页显示条数
Page<User> page = new Page<>(1, 2);
// 调用IService的page方法,实现分页查询
Page<User> userPage = userService.page(page);
// 获取分页结果
List<User> records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总条数
long pages = userPage.getPages(); // 总页数
System.out.println("总条数:" + total);
System.out.println("总页数:" + pages);
System.out.println("当前页数据:");
records.forEach(user -> System.out.println(user));
}
执行测试后,控制台会打印带有limit关键字的SQL语句,分页功能正常生效。
坑4:逻辑删除配置后,删除方法失效
场景:
配置逻辑删除后,执行删除方法,数据库中的数据没有被真正删除,但也没有报错,查询时却无法查询到该数据。
原因分析(李哥拆解):
逻辑删除的核心是“假删除”——删除时不删除数据,而是修改逻辑删除字段(比如is_deleted)的值,查询时自动过滤逻辑删除的数据。新手容易忘记配置逻辑删除字段,或者配置错误,导致删除方法失效。
解决方法(李哥手把手教):
- 给t_user表添加逻辑删除字段is_deleted(0=未删除,1=已删除):
ALTER TABLE `t_user` ADD COLUMN `is_deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除(0=未删除,1=已删除)';
- 在实体类中添加逻辑删除字段,并添加@TableLogic注解:
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private Integer age;
private String email;
private LocalDateTime createTime;
// 逻辑删除字段,@TableLogic注解标识
@TableLogic
private Integer isDeleted;
}
- 在application.yml中配置逻辑删除相关参数(可选,使用默认值可省略):
mybatis-plus:
global-config:
db-config:
logic-delete-field: isDeleted # 逻辑删除字段名(与实体类一致)
logic-delete-value: 1 # 逻辑删除值(已删除)
logic-not-delete-value: 0 # 逻辑未删除值(未删除)
配置完成后,再次执行删除方法,会发现数据库中的is_deleted字段被修改为1,数据未被真正删除,查询时会自动过滤is_deleted=1的数据,逻辑删除功能正常生效。
第三章:精通之路——MyBatis-Plus高级用法,提升开发效率
解决了新手常踩的“坑”后,我已经能熟练运用MyBatis-Plus完成基础CRUD操作,但李哥告诉我:“这还不够,MyBatis-Plus的高级用法,能帮你节省大量时间,提升开发效率,这才是从‘会用’到‘精通’的关键。”
接下来的一段时间,李哥带着我,逐一掌握了MyBatis-Plus的高级用法,每一个都能直接用到实际工作中,彻底摆脱“工具人”的命运。
一、条件构造器:灵活构建复杂查询(开发高频)
在实际开发中,我们很少会查询所有数据,更多的是根据条件查询(比如根据用户名模糊查询、根据年龄范围查询、多条件组合查询)。MyBatis-Plus提供的条件构造器,能让我们无需编写SQL,通过链式调用,灵活构建复杂的查询条件。
李哥告诉我,新手最常用的两个条件构造器是:QueryWrapper(普通条件构造器)和LambdaQueryWrapper(Lambda条件构造器),其中LambdaQueryWrapper更推荐,能避免字段名写错,提升代码可读性。
1. LambdaQueryWrapper(推荐,新手首选)
以“用户查询”为例,李哥帮我整理了常用的条件查询场景,代码可直接复制使用:
// 在UserCrudTest类中添加测试方法
@Test
public void testLambdaQuery() {
// 1. 模糊查询:用户名包含“wang”,且年龄大于等于20
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(User::getUsername, "wang") // 模糊查询
.ge(User::getAge, 20); // 大于等于
List<User> userList = userService.list(lambdaQueryWrapper);
System.out.println("模糊查询结果:");
userList.forEach(System.out::println);
// 2. 多条件组合:年龄在18-30之间,且邮箱不为null,且未被逻辑删除
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.between(User::getAge, 18, 30) // 年龄在18-30之间
.isNotNull(User::getEmail) // 邮箱不为null
.eq(User::getIsDeleted, 0); // 未被逻辑删除
List<User> list = userService.list(wrapper);
System.out.println("多条件组合查询结果:");
list.forEach(System.out::println);
// 3. 排序查询:按创建时间降序排列,取前3条数据
LambdaQueryWrapper<User> sortWrapper = new LambdaQueryWrapper<>();
sortWrapper.orderByDesc(User::getCreateTime) // 按创建时间降序
.last("limit 3"); // 拼接SQL片段(谨慎使用,避免SQL注入)
List<User> sortList = userService.list(sortWrapper);
System.out.println("排序查询结果:");
sortList.forEach(System.out::println);
}
2. 常用条件构造器方法(新手必记)
李哥帮我整理了新手常用的条件构造器方法,无需死记硬背,用到时查阅即可:
| 方法 | 作用 |
|---|---|
| eq | 等于(=),比如eq(User::getAge, 20) → age = 20 |
| ne | 不等于(≠),比如ne(User::getAge, 20) → age ≠ 20 |
| like | 模糊查询(%值%),比如like(User::getUsername, “wang”) → username like ‘%wang%’ |
| likeLeft | 左模糊查询(%值),比如likeLeft(User::getUsername, “wang”) → username like ‘%wang’ |
| likeRight | 右模糊查询(值%),比如likeRight(User::getUsername, “wang”) → username like ‘wang%’ |
| ge | 大于等于(≥),比如ge(User::getAge, 20) → age ≥ 20 |
| gt | 大于(>),比如gt(User::getAge, 20) → age > 20 |
| le | 小于等于(≤),比如le(User::getAge, 30) → age ≤ 30 |
| lt | 小于(<),比如lt(User::getAge, 30) → age < 30 |
| between | 在某个范围之间,比如between(User::getAge, 18, 30) → age between 18 and 30 |
| isNotNull | 字段不为null,比如isNotNull(User::getEmail) → email is not null |
| isNull | 字段为null,比如isNull(User::getEmail) → email is null |
| orderByAsc | 按字段升序排列,比如orderByAsc(User::getCreateTime) → order by create_time asc |
| orderByDesc | 按字段降序排列,比如orderByDesc(User::getCreateTime) → order by create_time desc |
二、自动填充:告别重复的时间字段赋值
在实际开发中,几乎所有实体类都有“创建时间(createTime)”“修改时间(updateTime)”字段,每次新增、修改数据时,都需要手动设置这些字段的值,非常繁琐,还容易遗漏。
李哥告诉我,MyBatis-Plus的自动填充功能,能自动为指定字段赋值,无需手动编写代码,彻底解决这个问题。下面是Spring Boot 3.x中自动填充的完整配置步骤,可直接复制使用。
1. 实体类添加自动填充注解
在需要自动填充的字段上,添加@TableField注解,指定fill属性(FieldFill.INSERT表示新增时填充,FieldFill.INSERT_UPDATE表示新增和修改时都填充):
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private Integer age;
private String email;
// 新增时自动填充(创建时间)
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 新增和修改时自动填充(修改时间)
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// 逻辑删除字段
@TableLogic
private Integer isDeleted;
}
2. 编写自动填充处理器
创建自动填充处理器类,实现MetaObjectHandler接口,重写insertFill(新增填充)和updateFill(修改填充)方法,李哥帮我整理了完整代码:
package com.example.demo.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
// 自动填充处理器,交给Spring管理
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 新增时填充
@Override
public void insertFill(MetaObject metaObject) {
// 填充createTime和updateTime字段,值为当前时间
strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
// 修改时填充
@Override
public void updateFill(MetaObject metaObject) {
// 填充updateTime字段,值为当前时间
strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
3. 测试自动填充功能
配置完成后,执行新增用户方法,无需手动设置createTime和updateTime,数据库中这两个字段会自动填充为当前时间;执行修改用户方法,updateTime会自动更新为当前时间,彻底告别手动赋值的繁琐。
三、代码生成器:一键生成CRUD代码,彻底解放双手
李哥告诉我,MyBatis-Plus的代码生成器(AutoGenerator)是新手的“神器”——它能根据数据库表结构,一键生成实体类、Mapper接口、Service、Controller等所有CRUD相关代码,无需手动编写,极大提升开发效率,尤其适合多模块、多表的大型项目。
下面是Spring Boot 3.2.x整合MyBatis-Plus代码生成器的完整配置,李哥帮我简化了配置,新手可直接复制使用,只需修改数据库连接信息和包路径即可。
1. 导入代码生成器依赖(pom.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. 编写代码生成器类
package com.example.demo.generator;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
public class MyBatisPlusGenerator {
public static void main(String[] args) {
// 1. 创建代码生成器对象
AutoGenerator mpg = new AutoGenerator();
// 2. 全局配置(核心配置,修改为自己的项目信息)
GlobalConfig gc = new GlobalConfig();
// 获取当前项目根路径(无需手动修改,自动获取)
String projectPath = System.getProperty("user.dir");
// 生成文件的输出目录(拼接路径,可根据自己的项目结构调整)
gc.setOutputDir(projectPath + "/src/main/java");
// 作者信息(填写自己的姓名/昵称)
gc.setAuthor("xiaowang");
// 生成后是否打开输出目录(true=打开,false=不打开,新手推荐true)
gc.setOpen(true);
// 生成实体类时,是否生成 serialVersionUID(序列化ID,推荐开启)
gc.setSwagger2(false); // 新手可先关闭,后续需要再开启
// 避免生成重复文件
gc.setFileOverride(true);
// 设置实体类、Mapper等文件的命名规则(无需修改,默认即可)
gc.setEntityName("%s"); // 实体类命名:User.java(不添加前缀)
gc.setMapperName("%sMapper"); // Mapper接口命名:UserMapper.java
gc.setServiceName("%sService"); // Service接口命名:UserService.java
gc.setServiceImplName("%sServiceImpl"); // Service实现类命名:UserServiceImpl.java
gc.setControllerName("%sController"); // Controller命名:UserController.java
// 将全局配置设置到生成器中
mpg.setGlobalConfig(gc);
// 3. 数据源配置(关键!修改为自己的MySQL连接信息)
DataSourceConfig dsc = new DataSourceConfig();
// 数据库类型(MySQL,固定写法)
dsc.setDbType(com.baomidou.mybatisplus.generator.config.rules.DbType.MYSQL);
// 数据库驱动类(MySQL 8.0 固定写法,无需修改)
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
// 数据库连接URL(修改为自己的数据库地址、数据库名)
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true");
// 数据库用户名(修改为自己的MySQL用户名,默认root)
dsc.setUsername("root");
// 数据库密码(修改为自己的MySQL密码)
dsc.setPassword("123456");
// 将数据源配置设置到生成器中
mpg.setDataSource(dsc);
// 4. 包配置(修改为自己的项目包路径)
PackageConfig pc = new PackageConfig();
// 父包名(核心!替换成自己的项目父包,比如com.example.demo)
pc.setParent("com.example.demo");
// 实体类包名(默认entity,无需修改)
pc.setEntity("entity");
// Mapper接口包名(默认mapper,无需修改)
pc.setMapper("mapper");
// Service接口包名(默认service,无需修改)
pc.setService("service");
// Service实现类包名(默认service.impl,无需修改)
pc.setServiceImpl("service.impl");
// Controller包名(默认controller,无需修改)
pc.setController("controller");
// 将包配置设置到生成器中
mpg.setPackageInfo(pc);
// 5. 策略配置(生成规则,新手无需修改,直接使用)
StrategyConfig strategy = new StrategyConfig();
// 指定要生成的数据库表名(关键!填写自己的表名,多个表用逗号分隔)
// 例:strategy.setInclude("t_user", "t_role", "t_menu")
strategy.setInclude("t_user");
// 排除不需要生成的表(没有则不写)
strategy.setExclude();
// 数据库表名映射到实体类的命名策略(下划线转驼峰,默认,无需修改)
strategy.setNaming(NamingStrategy.underline_to_camel);
// 数据库表字段名映射到实体类属性的命名策略(下划线转驼峰,默认,无需修改)
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 实体类是否添加@Data注解(lombok,推荐开启,需导入lombok依赖)
strategy.setEntityLombokModel(true);
// 实体类是否添加逻辑删除字段(如果表中有is_deleted字段,开启此配置)
strategy.setLogicDeleteFieldName("isDeleted");
// Controller是否开启Rest风格(@RestController,推荐开启)
strategy.setRestControllerStyle(true);
// 去除Controller类名的"Controller"后缀(false=保留,true=去除,新手推荐false)
strategy.setControllerMappingHyphenStyle(false);
// 将策略配置设置到生成器中
mpg.setStrategy(strategy);
// 6. 模板引擎配置(默认使用Velocity,无需修改)
mpg.setTemplateEngine(new VelocityTemplateEngine());
// 7. 执行代码生成(一键生成,点击运行此类的main方法即可)
mpg.execute();
}
}
3. 代码生成器使用步骤(新手必看)
配置完成后,使用方法非常简单,李哥手把手教我操作,3步即可生成所有CRUD代码:
-
确认pom.xml中已导入代码生成器依赖和lombok依赖(前面已配置,无需重复操作);
-
修改MyBatisPlusGenerator类中的3处核心配置:① 全局配置中的作者信息;② 数据源配置中的MySQL连接信息(URL、用户名、密码);③ 策略配置中的数据库表名(setInclude方法中填写自己的表名);
-
运行MyBatisPlusGenerator类的main方法,等待1-2秒,代码会自动生成到指定的包路径下,生成的文件包括:实体类、Mapper接口、Mapper XML文件(可选)、Service接口、Service实现类、Controller类。
4. 生成代码说明(新手必懂)
生成完成后,无需手动修改任何代码,即可直接使用,李哥帮我梳理了生成的核心文件及作用:
-
entity:实体类,已自动添加@Data、@TableName、@TableId等注解,逻辑删除字段、自动填充字段也会自动生成(需提前在表中添加对应字段);
-
mapper:Mapper接口,已自动继承BaseMapper,包含所有基础CRUD方法,无需手动编写;
-
service:Service接口和实现类,已自动继承IService和ServiceImpl,基础CRUD方法已实现,可直接调用;
-
controller:Controller类,已自动生成REST风格的接口(get/post请求),包含基础的CRUD接口,可根据业务需求修改。
5. 注意事项
李哥特别提醒我,使用代码生成器时,有2个细节新手容易忽略,避免踩坑:
-
生成代码前,需确保数据库表已创建,且表字段与实体类字段对应(代码生成器会根据表结构自动生成实体类,字段名自动下划线转驼峰);
-
如果多次运行生成器,会覆盖已有的代码,建议生成后不要轻易重新运行,若需修改表结构,可删除原有生成文件后再重新生成。
四、乐观锁:解决并发修改冲突(实战必备)
在实际项目中,并发修改是非常常见的场景——比如两个用户同时修改同一个用户的信息,第一个用户修改后,第二个用户的修改会覆盖第一个用户的结果,导致数据不一致。李哥告诉我,MyBatis-Plus的乐观锁功能,能轻松解决这个问题,无需手动编写并发控制代码。
乐观锁的核心原理:在实体类中添加一个版本字段(比如version),每次修改数据时,版本号自动加1,修改时会判断当前版本号是否与数据库中的版本号一致,一致则修改,不一致则修改失败(说明数据已被其他用户修改)。
1. 配置乐观锁(3步完成)
(1)给t_user表添加版本字段version(默认值为1):
ALTER TABLE `t_user` ADD COLUMN `version` int NOT NULL DEFAULT 1 COMMENT '乐观锁版本号';
(2)在实体类中添加version字段,并添加@Version注解:
package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private Integer age;
private String email;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer isDeleted;
// 乐观锁版本字段,@Version注解标识
@Version
private Integer version;
}
(3)在分页插件配置类中,添加乐观锁插件(与分页插件配置在一起,无需单独创建类):
package com.example.demo.config;
import com.baomidou.mybatisplus.annotation.DbType;
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(DbType.MYSQL));
// 乐观锁插件(添加这一行即可)
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
2. 测试乐观锁功能
配置完成后,李哥帮我编写了并发修改的测试方法,模拟两个用户同时修改同一个用户的信息,测试乐观锁是否生效:
// 在UserCrudTest类中添加测试方法
@Test
public void testOptimisticLocker() {
// 1. 第一个用户查询用户信息(此时version=1)
User user1 = userService.getById(1L);
// 2. 第二个用户同时查询同一个用户信息(此时version也=1)
User user2 = userService.getById(1L);
// 3. 第一个用户修改年龄为25,修改成功后version自动变为2
user1.setAge(25);
userService.updateById(user1);
// 4. 第二个用户修改年龄为26,此时数据库中version已变为2,与user2的version=1不一致,修改失败
user2.setAge(26);
boolean update = userService.updateById(user2);
System.out.println("第二个用户修改结果:" + (update ? "成功" : "失败"));
// 5. 查询最终结果,年龄为25(第一个用户的修改生效,第二个用户的修改失败)
User finalUser = userService.getById(1L);
System.out.println("最终用户年龄:" + finalUser.getAge());
}
3. 测试结果说明
执行测试后,控制台会打印“第二个用户修改结果:失败”,最终用户年龄为25,说明乐观锁已生效。这样就避免了并发修改导致的数据不一致问题,无需手动编写复杂的并发控制代码,极大简化了开发。
第四章:实战总结——从新手到精通的成长路径
跟着李哥学习MyBatis-Plus的这段时间,我从一个“被CRUD折磨的工具人”,变成了能轻松掌控数据库操作的开发者,不仅节省了大量开发时间,还提升了代码的优雅度和可维护性。结合自己的学习经历,李哥帮我总结了MyBatis-Plus的学习路径,新手可以参考这个路径,快速上手、精通:
第一步:夯实基础(1-2天)
-
理解MyBatis-Plus的核心定位:MyBatis的增强工具,完全兼容MyBatis,核心是“简化开发”;
-
完成Spring Boot 3.x与MyBatis-Plus的环境整合,掌握依赖导入、配置文件编写、启动类注解配置;
-
掌握一键CRUD的使用:继承BaseMapper和IService,实现基础的新增、删除、修改、查询操作,能独立完成测试。
第二步:避坑进阶(2-3天)
-
重点解决新手常踩的4个坑:主键自增失败、字段映射失败、分页失效、逻辑删除配置错误;
-
熟练掌握条件构造器的使用,尤其是LambdaQueryWrapper,能灵活构建复杂的查询条件;
-
掌握自动填充功能,实现时间字段的自动赋值,告别手动设置。
第三步:精通高级用法(3-4天)
-
熟练使用代码生成器,能根据数据库表结构,一键生成所有CRUD相关代码,提升开发效率;
-
掌握乐观锁的配置和使用,解决并发修改冲突问题;
-
了解MyBatis-Plus的其他高级功能,比如拦截器、自定义SQL、多表查询等(根据业务需求深入学习)。
最后:实战落地(持续练习)
李哥说,学习MyBatis-Plus的核心是“多练、多踩坑、多总结”。新手可以搭建一个简单的管理系统(比如用户管理、订单管理),将所学的知识点逐一应用到实战中,遇到问题多排查、多查阅官方文档(MyBatis-Plus官方文档),久而久之,就能熟练运用MyBatis-Plus,彻底摆脱重复CRUD的困扰。
写在最后:MyBatis-Plus的核心价值,是让开发者从重复、繁琐的CRUD操作中解放出来,把更多的精力放在核心业务逻辑上。它不是“花里胡哨”的工具,而是能真正提升开发效率、降低开发成本的“实战神器”。希望这篇实战指南,能帮助和曾经的我一样的Java新手,快速掌握MyBatis-Plus,少走弯路,早日摆脱“工具人”的命运,成为更优秀的开发者!
更多推荐




所有评论(0)