一、引言
在 SpringBoot 开发中,Mybatis 一直是国内最流行的持久层框架。但它有个让人头疼的地方:每写一个简单的增删改查,都要去 XML 或注解里手写一遍 SQL,即使只是 select * from user where id = ? 这种毫无技术含量的语句。
Mybatis-Plus(简称 MP)就是为「干掉重复的简单 SQL」而生的。它在 Mybatis 的基础上只做增强、不做改变,能让我们用极少量的代码完成绝大多数 CRUD 操作。本文就从零带你上手 Mybatis-Plus。
二、Mybatis-Plus 是什么
Mybatis-Plus 是一个 Mybatis 的增强工具,由国人开发(作者「青苗」),目前已经是国内使用最广的 ORM 增强框架之一。核心思想:在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
它提供了以下核心能力:
- 通用
BaseMapper:内置单表的增删改查,无需写任何 SQL; - 强大的条件构造器:
QueryWrapper/LambdaQueryWrapper链式拼条件; - 内置分页插件;
- 代码生成器、逻辑删除、乐观锁、自动填充等开箱即用的功能。
三、相比原生 Mybatis 的优势
| 对比项 | 原生 Mybatis | Mybatis-Plus |
|---|---|---|
| 简单 CRUD | 需手写 SQL 或注解 | BaseMapper 一键搞定 |
| 分页 | 需引入 PageHelper 或手写 | 内置分页插件 |
| 条件拼接 | 动态 SQL 手写 <if> 等 |
条件构造器链式调用 |
| 字段名书写 | 字符串易写错 | Lambda 方式类型安全 |
| 代码量 | 大 | 小 |
一句话总结:**原生 Mybatis 该有的能力它全保留,繁琐的简单 CRUD 它全替你做了。**
四、快速上手:集成 SpringBoot
4.1 引入依赖
本文使用 SpringBoot 3.x,Mybatis-Plus 需使用 mybatis-plus-spring-boot3-starter:
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 简化实体类,非必须,但强烈推荐 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
注意:如果是 SpringBoot 2.x,请使用
mybatis-plus-boot-starter。
4.2 配置数据源
application.yml:
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
# 控制台打印 SQL,方便调试
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto # 主键自增
4.3 准备数据表
sql
CREATE DATABASE IF NOT EXISTS mp_demo DEFAULT CHARACTER SET utf8mb4;
USE mp_demo;
CREATE TABLE `user` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` VARCHAR(50) DEFAULT 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='用户表';
4.4 实体类 User
java
package com.example.mp.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
@TableName("user") // 指定对应表名(类名与表名不一致时必填)
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private Integer age;
private String email;
private LocalDateTime createTime;
}
4.5 Mapper 接口
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;
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,单表 CRUD 已经全部内置,这里可以什么都不写
}
启动类记得加 @MapperScan(或每个 Mapper 上手动加 @Mapper,二选一):
java
package com.example.mp;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mp.mapper")
public class MpDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MpDemoApplication.class, args);
}
}
五、BaseMapper 通用 CRUD
继承 BaseMapper 后,我们就白拿了大量通用方法。看一个完整的 Service 示例:
java
package com.example.mp.service;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/** 增:插入一条记录 */
public void insert() {
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
user.setCreateTime(LocalDateTime.now());
userMapper.insert(user);
// 插入成功后,自增主键会自动回填到 user.getId()
System.out.println("插入成功,主键 = " + user.getId());
}
/** 删:按主键删 / 批量删 */
public void delete() {
userMapper.deleteById(1L); // 按主键删除
userMapper.deleteBatchIds(Arrays.asList(2L, 3L)); // 按主键批量删除
}
/** 改:按主键更新(只更新非 null 字段) */
public void update() {
User user = new User();
user.setId(1L);
user.setAge(30); // 只更新 age 字段,其余字段为 null 不会被更新
userMapper.updateById(user);
}
/** 查:按主键查 / 批量查 / 查所有 */
public void select() {
User user = userMapper.selectById(1L);
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
List<User> all = userMapper.selectList(null); // 传 null 表示无条件,查全部
long count = userMapper.selectCount(null); // 查总数
}
}
BaseMapper 还提供了 insertBatch、update、selectPage、selectOne 等数十个方法,覆盖了绝大多数单表操作场景。
六、条件构造器:QueryWrapper 与 LambdaQueryWrapper
当查询条件变复杂时,就需要条件构造器登场了。
java
package com.example.mp.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class WrapperDemoService {
@Autowired
private UserMapper userMapper;
public void queryWrapperDemo() {
// 方式一:QueryWrapper ------ 字段名以字符串形式书写
QueryWrapper<User> qw = new QueryWrapper<>();
qw.eq("age", 20) // age = 20
.like("name", "张") // name LIKE '%张%'
.between("age", 18, 30) // age BETWEEN 18 AND 30
.orderByDesc("id"); // ORDER BY id DESC
List<User> list1 = userMapper.selectList(qw);
// 方式二:LambdaQueryWrapper ------ 基于 Lambda,类型安全,字段名不会写错
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.eq(User::getAge, 20)
.like(User::getName, "张")
.between(User::getAge, 18, 30)
.orderByDesc(User::getId);
List<User> list2 = userMapper.selectList(lqw);
}
}
常用条件方法速查:
| 方法 | 含义 | 示例 |
|---|---|---|
eq |
等于 = | eq("age", 20) |
ne |
不等于 != | ne("age", 20) |
gt / ge |
大于 / 大于等于 | gt("age", 18) |
lt / le |
小于 / 小于等于 | le("age", 30) |
like / likeLeft / likeRight |
模糊匹配 | like("name", "张") |
between |
区间 | between("age", 18, 30) |
in |
集合 | in("id", 1, 2, 3) |
isNull / isNotNull |
判空 | isNull("email") |
orderByAsc / orderByDesc |
排序 | orderByDesc("id") |
推荐日常使用 LambdaQueryWrapper:一旦字段名重命名,编译器会直接报错,避免运行时才暴露的字符串拼写问题。
七、分页插件配置
Mybatis-Plus 的分页需要先注册一个插件,非常轻量。
配置类:
java
package com.example.mp.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 {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件:指定数据库类型为 MySQL
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用示例:
java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PageDemoService {
@Autowired
private UserMapper userMapper;
public void pageDemo() {
Page<User> page = new Page<>(1, 5); // 第 1 页,每页 5 条
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.orderByDesc(User::getId);
Page<User> result = userMapper.selectPage(page, lqw);
System.out.println("总记录数:" + result.getTotal());
System.out.println("总页数:" + result.getPages());
System.out.println("当前页数据:" + result.getRecords());
}
}
八、总结
Mybatis-Plus 的价值可以概括为一句话:它让「写 SQL」这件事从每天必做,变成了只有复杂场景才需要做的事。
本文我们完成了:
- 认识了 Mybatis-Plus 及它相对原生 Mybatis 的优势;
- 完成了 SpringBoot 集成(依赖、配置、实体、Mapper);
- 用
BaseMapper实现了零 SQL 的通用 CRUD; - 用
QueryWrapper/LambdaQueryWrapper拼复杂条件; - 配置并使用了内置分页插件。
掌握这些,日常单表开发基本可以告别手写 SQL 了。