一、为什么我们需要 MyBatis-Plus?
在讲 MyBatis-Plus 之前,我们先看看原生 MyBatis 有哪些痛点:
- 大量重复代码:每个表都要写重复的 CRUD 代码,开发效率低
- XML 文件臃肿:简单的查询也要写 SQL,XML 文件越来越大,难以维护
- 分页功能繁琐:需要手动写分页 SQL,或者集成第三方插件
- 条件查询麻烦:动态条件查询需要写大量的 if 判断,代码可读性差
- 没有统一的字段处理:比如创建时间、更新时间这些公共字段,每次都要手动设置
而 MyBatis-Plus 就是为了解决这些痛点而生的。它是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis-Plus 的核心优势
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
- 损耗小:启动即会自动注入基本 CRUD,性能基本无损耗
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,只需要少量配置就能实现单表的大部分 CRUD 操作
- 强大的条件构造器:可以轻松构建各种复杂的查询条件,不需要写 SQL
- 内置分页插件:基于 MyBatis 物理分页,配置简单,不需要写分页 SQL
- 丰富的插件:支持逻辑删除、自动填充、乐观锁、多数据源等插件
- 代码生成器:一键生成实体类、Mapper、Service、Controller 层代码
二、Spring Boot 集成 MyBatis-Plus
集成 MyBatis-Plus 非常简单,只需要三步就能完成。
第一步:引入依赖
xml
<!-- MyBatis-Plus启动器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
第二步:配置数据库连接
在application.yml中添加数据库连接配置:
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: 123456
# MyBatis-Plus配置
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 开启下划线转驼峰
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL打印
global-config:
db-config:
id-type: auto # 主键自增
logic-delete-field: deleted # 全局逻辑删除字段名
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
第三步:添加 Mapper 扫描注解
在启动类上添加@MapperScan注解,扫描 Mapper 接口:
java
运行
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
}
第四步:编写实体类和 Mapper 接口
java
运行
// 实体类
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
// Mapper接口
public interface UserMapper extends BaseMapper<User> {
}
没错,就这么简单!现在你已经可以使用 UserMapper 进行所有的单表 CRUD 操作了,不需要写任何 XML 文件和 SQL 语句。
三、基本 CRUD 操作
MyBatis-Plus 的 BaseMapper 接口提供了 17 个基本的 CRUD 方法,覆盖了所有常见的单表操作。
1. 插入操作
java
运行
// 插入一条记录
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
userMapper.insert(user);
2. 更新操作
java
运行
// 根据ID更新
User user = new User();
user.setId(1L);
user.setAge(21);
userMapper.updateById(user);
// 根据条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "张三").set("email", "zhangsan_new@example.com");
userMapper.update(null, updateWrapper);
3. 删除操作
java
运行
// 根据ID删除
userMapper.deleteById(1L);
// 根据ID批量删除
userMapper.deleteBatchIds(Arrays.asList(1L, 2L, 3L));
// 根据条件删除
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 20);
userMapper.delete(queryWrapper);
4. 查询操作
java
运行
// 根据ID查询
User user = userMapper.selectById(1L);
// 根据ID批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
// 查询所有
List<User> allUsers = userMapper.selectList(null);
// 根据条件查询一条记录
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", "张三");
User oneUser = userMapper.selectOne(queryWrapper);
// 根据条件查询列表
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18).orderByDesc("create_time");
List<User> userList = userMapper.selectList(queryWrapper);
// 查询总记录数
Long count = userMapper.selectCount(null);
四、核心功能详解
1. 条件构造器(Wrapper)
条件构造器是 MyBatis-Plus 最强大的功能之一。它可以让你不用写 SQL,就能构建各种复杂的查询条件。
MyBatis-Plus 提供了两种常用的条件构造器:
QueryWrapper:用于查询、删除操作UpdateWrapper:用于更新操作
常用的条件方法
java
运行
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 等于
wrapper.eq("name", "张三");
// 不等于
wrapper.ne("name", "张三");
// 大于
wrapper.gt("age", 18);
// 大于等于
wrapper.ge("age", 18);
// 小于
wrapper.lt("age", 30);
// 小于等于
wrapper.le("age", 30);
// 模糊查询
wrapper.like("name", "张");
// 左模糊
wrapper.likeLeft("name", "张");
// 右模糊
wrapper.likeRight("name", "张");
// 包含
wrapper.in("age", Arrays.asList(18, 20, 22));
// 不包含
wrapper.notIn("age", Arrays.asList(18, 20, 22));
// 范围查询
wrapper.between("age", 18, 30);
// 不为空
wrapper.isNotNull("email");
// 为空
wrapper.isNull("email");
// 排序
wrapper.orderByAsc("age");
wrapper.orderByDesc("create_time");
// 分组
wrapper.groupBy("age");
// 分页
wrapper.last("limit 10");
动态条件查询
条件构造器支持链式调用和动态条件,非常适合处理复杂的查询场景:
java
运行
public List<User> searchUsers(String name, Integer minAge, Integer maxAge) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 动态条件:如果参数不为空,才添加条件
if (StringUtils.hasText(name)) {
wrapper.like("name", name);
}
if (minAge != null) {
wrapper.ge("age", minAge);
}
if (maxAge != null) {
wrapper.le("age", maxAge);
}
wrapper.orderByDesc("create_time");
return userMapper.selectList(wrapper);
}
2. 分页插件
MyBatis-Plus 内置了强大的分页插件,只需要简单配置就能使用。
第一步:配置分页插件
java
运行
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
第二步:使用分页查询
java
运行
// 分页查询:第1页,每页10条
Page<User> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);
Page<User> userPage = userMapper.selectPage(page, wrapper);
// 获取分页数据
List<User> records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总记录数
long pages = userPage.getPages(); // 总页数
long current = userPage.getCurrent(); // 当前页
long size = userPage.getSize(); // 每页条数
3. 逻辑删除
逻辑删除是指不是真正的从数据库中删除数据,而是通过一个字段标记数据是否被删除。MyBatis-Plus 内置了逻辑删除功能,只需要简单配置就能使用。
第一步:在数据库表中添加逻辑删除字段
sql
ALTER TABLE user ADD COLUMN deleted INT DEFAULT 0 COMMENT '逻辑删除字段:0-未删除,1-已删除';
第二步:在实体类中添加注解
java
运行
@Data
@TableName("user")
public class User {
// 其他字段...
@TableLogic
private Integer deleted;
}
现在,所有的删除操作都会自动变成更新操作,更新deleted字段为 1;所有的查询操作都会自动过滤掉已删除的数据。
4. 自动填充
自动填充功能可以自动为创建时间、更新时间这些公共字段赋值,不需要每次手动设置。
第一步:在实体类中添加注解
java
运行
@Data
@TableName("user")
public class User {
// 其他字段...
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充
private LocalDateTime updateTime;
}
第二步:实现元对象处理器
java
运行
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);
}
}
现在,当你插入或更新数据时,createTime和updateTime字段会自动填充当前时间。
5. 代码生成器
MyBatis-Plus 的代码生成器可以一键生成实体类、Mapper、Service、Controller 层的代码,大大提高开发效率。
第一步:引入代码生成器依赖
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
第二步:编写代码生成器
java
运行
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus_demo", "root", "123456")
.globalConfig(builder -> {
builder.author("your name") // 设置作者
.outputDir(System.getProperty("user.dir") + "/src/main/java") // 输出目录
.disableOpenDir(); // 生成后不打开目录
})
.packageConfig(builder -> {
builder.parent("com.example.demo") // 父包名
.entity("entity") // 实体类包名
.mapper("mapper") // Mapper包名
.service("service") // Service包名
.controller("controller"); // Controller包名
})
.strategyConfig(builder -> {
builder.addInclude("user", "order", "product") // 需要生成的表名
.entityBuilder()
.enableLombok() // 开启Lombok
.logicDeleteColumnName("deleted") // 逻辑删除字段名
.controllerBuilder()
.enableRestStyle(); // 开启RestController
})
.execute();
}
}
运行这个 main 方法,就能一键生成所有层的代码,非常方便。
五、常见问题与解决方案
问题 1:分页插件不生效
常见原因:
- 没有配置分页插件
- 分页插件配置错误
- 使用了自定义的 SQL,没有走 MyBatis-Plus 的分页
解决方案:
- 确保正确配置了
MybatisPlusInterceptor和PaginationInnerInterceptor - 确保数据库类型配置正确
- 自定义 SQL 需要使用
Page作为参数,并且返回Page对象
问题 2:条件构造器的字段名写错
问题描述:条件构造器中使用了 Java 字段名,而不是数据库字段名,导致 SQL 错误。
解决方案:
- 条件构造器中使用的是数据库字段名,不是 Java 字段名
- 如果开启了下划线转驼峰,数据库字段名是下划线格式,Java 字段名是驼峰格式
问题 3:逻辑删除后查询不到数据
问题描述:逻辑删除后,查询时没有自动过滤已删除的数据。
解决方案:
- 确保实体类上添加了
@TableLogic注解 - 确保全局配置中正确配置了逻辑删除字段
- 自定义 SQL 需要手动添加逻辑删除条件
问题 4:自动填充不生效
常见原因:
- 没有实现
MetaObjectHandler接口 - 实现类没有添加
@Component注解 - 字段上没有添加
@TableField(fill = ...)注解
解决方案:
- 检查以上三点,确保配置正确
- 确保字段类型和填充值的类型一致
六、生产环境最佳实践
- 不要过度使用条件构造器:对于非常复杂的查询,建议直接写 XML SQL,可读性和可维护性更好
- 合理使用代码生成器:代码生成器可以生成基础代码,但业务逻辑还是需要自己写
- 开启 SQL 打印:开发环境开启 SQL 打印,方便调试和排查问题
- 使用分页插件:所有的分页查询都使用 MyBatis-Plus 的分页插件,不要自己写分页 SQL
- 使用逻辑删除:生产环境建议使用逻辑删除,不要物理删除数据
- 使用自动填充:创建时间、更新时间这些公共字段使用自动填充
- 避免使用 selectByIds 查询大量数据:如果 ID 数量很多,会导致 SQL 过长,性能下降
- 定期优化 SQL:使用慢查询日志,找出执行慢的 SQL 进行优化
- 不要在 Service 层直接使用 Mapper:建议在 Service 层封装业务逻辑,Controller 层调用 Service 层
- 统一异常处理:结合全局异常处理,处理 MyBatis-Plus 抛出的异常
总结
MyBatis-Plus 是一个非常优秀的 ORM 框架,它大大简化了 MyBatis 的开发,让我们从繁琐的 CRUD 代码中解放出来,专注于业务逻辑的实现。
回顾一下本文的核心内容:
- MyBatis-Plus 是 MyBatis 的增强工具,解决了原生 MyBatis 的诸多痛点
- 集成 MyBatis-Plus 非常简单,只需要三步就能完成
- BaseMapper 提供了 17 个基本的 CRUD 方法,覆盖了所有常见的单表操作
- 条件构造器可以轻松构建各种复杂的查询条件,不需要写 SQL
- 内置分页插件、逻辑删除、自动填充、代码生成器等强大功能
- 生产环境中要注意避免常见的坑,遵循最佳实践