


专栏:JavaEE 进阶跃迁营
个人主页:手握风云
目录
[一、MyBatis-Plus 介绍](#一、MyBatis-Plus 介绍)
[2.1. 准备工作](#2.1. 准备工作)
[2.2. 编码](#2.2. 编码)
[2.3. CRUD 单元测试](#2.3. CRUD 单元测试)
[三、MyBatis-Plus 复杂操作](#三、MyBatis-Plus 复杂操作)
[3.1. 常见注解](#3.1. 常见注解)
[1. @TableName](#1. @TableName)
[2. @TableField](#2. @TableField)
[3. @TableId](#3. @TableId)
[3.2. 打印日志](#3.2. 打印日志)
[3.3. 条件构造器](#3.3. 条件构造器)
[1. QueryWrapper](#1. QueryWrapper)
[2. UpdateWrapper](#2. UpdateWrapper)
[3. LambdaQueryWrapper](#3. LambdaQueryWrapper)
[4. LambdaUpdateWrapper](#4. LambdaUpdateWrapper)
一、MyBatis-Plus 介绍
MyBatis-Plus(简称 MP)是一个基于 MyBatis 的增强工具,其核心理念是在 MyBatis 的基础上"只做增强不做改变",专为简化开发和提高效率而生。它致力于成为 MyBatis 的最佳搭档,被形象地比喻为游戏魂斗罗中的 1P 和 2P,通过强强联合实现效率翻倍。
MP 具备多项显著特性。首先是无侵入性 ,引入该框架不会对现有工程产生负面影响,能够"润物无声"地融入项目。其次是效率至上 ,开发者只需进行简单配置,即可快速完成单表的 CRUD 操作,从而节省大量编写基础 SQL 的时间。此外,MP 功能丰富 ,内置了代码生成器、自动分页插件、逻辑删除、自动填充以及性能分析拦截器等一应俱全的实用功能。针对复杂的数据库操作,MP 提供了强大的条件构造器(Wrapper),支持通过 Lambda 表达式引用实体类属性,既避免了硬编码字段名的风险,又极大地提升了代码的可读性与维护性。

二、快速上手
2.1. 准备工作
sql
-- 1. 删除已存在的数据库(避免冲突)
DROP DATABASE IF EXISTS mybatis_test;
-- 2. 创建数据库并指定默认字符集
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 3. 使用目标数据库
USE mybatis_test;
-- 4. 删除已存在的用户表(避免冲突)
DROP TABLE IF EXISTS user_info;
-- 5. 创建用户表 user_info
CREATE TABLE user_info (
`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
`user_name` VARCHAR ( 127 ) NOT NULL,
`password` VARCHAR ( 127 ) NOT NULL,
`age` TINYINT ( 4 ) NOT NULL,
`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-女 0-默认',
`phone` VARCHAR ( 15 ) DEFAULT NULL,
`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now() ON UPDATE now(),
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
-- 6. 插入4条测试用户数据
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
在 pom.xml 文件添加以下依赖:
XML
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot4-starter</artifactId>
<version>3.5.15</version>
</dependency>
配置数据库:
XML
spring:
datasource:
driver-class-name: org.h2.Driver
username: root
password: test
sql:
init:
schema-locations: classpath:db/schema-h2.sql
data-locations: classpath:db/data-h2.sql
2.2. 编码
java
package com.yang.test2_14_1.model;
import lombok.Data;
import java.util.Date;
@Data
public class UserInfo {
private Integer id;
private String userName;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}
java
package com.yang.test2_14_1.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yang.test2_14_1.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
UserInfoMapper 继承了 BaseMapper 接口,并指定了泛型为 UserInfo。这意味着 UserInfoMapper 直接拥有了 BaseMapper 中定义的所有通用数据库操作方法,而无需编写任何 XML 映射文件或 SQL 语句。
BaseMapper 接口中定义了诸如 insert、deleteById、updateById、selectById 等方法。MyBatis-Plus 内部维护了一套 SQL 逻辑,当代理对象执行这些方法时,会根据传入的泛型实体类的元数据自动构建并生成相应的 SQL 语句。

java
package com.yang.test2_14_1;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.yang.test2_14_1.mapper")
public class Test2141Application {
public static void main(String[] args) {
SpringApplication.run(Test2141Application.class, args);
}
}
@MapperScan 指定 MyBatis 的 Mapper 接口所在的包路径。Spring Boot 启动时会自动扫描 对应包下的所有接口,并将它们动态代理生成实现类,注册到 Spring 容器中。这样你就可以在其他地方(如 Service 层)直接通过 @Autowired 注入这些 Mapper 接口了。
2.3. CRUD 单元测试
java
@Test
public void testSelectByIds() {
List<UserInfo> userInfos = userInfoMapper.selectByIds(List.of(1, 8));
userInfos.forEach(System.out::println);
}
selectByIds 方法用于根据主键 ID 集合查询数据,底层框架会根据传入的 ID 列表,动态生成 SQL 语句。

java
@Test
public void testInsert() {
UserInfo userInfo = new UserInfo();
userInfo.setUserName("Sundar");
userInfo.setPassword("Pichai");
userInfo.setGender(1);
userInfo.setAge(56);
int rows = userInfoMapper.insert(userInfo);
System.out.println("影响的行数:" + rows);
}
这里的执行结果生成的 id 是随机的,只需要保证 id 不重复即可。我们要想保证 id 字段是自增的,可以在 id 字段里面加入注解 @TableId。

java
@Data
public class UserInfo {
@TableId(type = IdType.AUTO)
private Integer id;
......
}
java
@Test
public void testUpdate() {
UserInfo userInfo = new UserInfo();
userInfo.setId(14);
userInfo.setUserName("bit33333");
userInfo.setPassword("bit33333");
userInfo.setPhone("15677777788");
userInfo.setDeleteFlag(1);
userInfoMapper.updateById(userInfo);
}
@Test
void testDelete(){
userInfoMapper.deleteById(14);
}
三、MyBatis-Plus 复杂操作
3.1. 常见注解
MyBatis-Plus 默认会按驼峰转蛇形 规则匹配实体类与数据库的表名、字段名,主键默认匹配id字段,当实体类和数据库的表 / 字段 / 主键命名不遵循该规则时,需通过专属注解显式标识对应关系。
1. @TableName
作用:指定实体类对应的数据库表名,解决实体类名与数据库表名不匹配的问题。
场景:当实体类名驼峰转蛇形后的名称与实际表名不一致时使用(如实体类 userinfo 转蛇形为 user_info,但实际表名为 user_info)。
java
package com.yang.test2_14_1.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.Date;
@Data
public class Userinfo {
@TableId(type = IdType.AUTO)
private Integer id;
private String userName;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}

在实体类上添加该注解,括号内填写数据库实际表名。
java
package com.yang.test2_14_1.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("user_info")
public class Userinfo {
@TableId(type = IdType.AUTO)
private Integer id;
private String userName;
private String password;
private Integer age;
private Integer gender;
private String phone;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}

2. @TableField
作用:指定实体类属性对应的数据库字段名,解决属性名与数据库字段名不匹配的问题。
场景:当实体类属性名驼峰转蛇形后的名称与实际字段名不一致时使用(如属性 deleteflag 转蛇形为 deleteflag,但实际字段名为 delete_flag)。
java
private Integer deleteflag;

java
@TableField("delete_flag")
private Integer deleteflag;
使用方式:在对应属性上添加该注解,括号内填写数据库实际字段名。

3. @TableId
作用:指定实体类属性对应的数据库主键字段名,解决主键属性名与数据库主键字段名不匹配的问题,是标识主键的专属注解。
场景:当实体类主键属性名并非默认的 id,与数据库主键字段名不一致时使用(如属性 userId,实际主键字段为 id)。
使用方式:在主键属性上添加该注解;若属性名与主键字段名一致,仅添加注解即可,无需指定字段名;若不一致,括号内填写数据库实际主键字段名。
java
@TableId(value = "id", type = IdType.AUTO)
private Integer userId;
3.2. 打印日志
为了方便学习,我们可以借助日志,查看Mybatis-Plus执行的SQL语句,参数和执行结果。
3.3. 条件构造器
MyBatis-Plus 提供的Wrapper 系列条件构造器,用于便捷构建复杂的数据库查询 / 更新 / 删除条件,支持链式调用,无需编写繁琐的原生 SQL,还能有效减少 SQL 注入风险。其核心抽象基类为 AbstractWrapper,提供了所有构造器的通用方法和属性,在此基础上衍生出四类常用的具体构造器,各有适用场景。

AbstractWrapper 是抽象基类,为其他 Wrapper 类提供共有方法与属性。QueryWrapper 专门构造查询条件,UpdateWrapper 用于构造更新条件,LambdaQueryWrapper 和 LambdaUpdateWrapper 则基于 Lambda 表达式,通过引用实体类属性避免硬编码字段名,提升代码可读性与可维护性。
1. QueryWrapper
核心特性:在AbstractWrapper基础上拓展了select方法,支持手动指定查询字段,可用于查询、更新、删除所有操作的条件构造,是最基础的条件构造器。
常用方法:提供各类条件匹配方法,eq(等于)、ne(不等于)、lt(小于)、gt(大于)、like(模糊匹配)、in(包含)等。
- lt:"less than" 的缩写,表示小于。
- le:"less than or equal to" 的缩写,表示小于等于。
- ge:"greater than or equal to" 的缩写,表示大于等于。
- gt:"greater than" 的缩写,表示大于。
- eq:"equals" 的缩写,表示等于。
- ne:"not equals" 的缩写,表示不等于。
java
@Test
void testQueryWrapper1() {
QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
queryWrapper.select("id", "username", "password", "age")
.eq("age", 18)
.like("username", "min");
List<Userinfo> userinfos = userInfoMapper.selectList(queryWrapper);
userinfos.forEach(System.out::println);
}
java
@Test
void testQueryWrapper2() {
QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 18);
userInfoMapper.delete(queryWrapper);
}
java
@Test
void testQueryWrapper3() {
QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
queryWrapper.lt("age", 20);
Userinfo userinfo = new Userinfo();
userinfo.setDeleteflag(1);
userInfoMapper.update(userinfo, queryWrapper);
}
2. UpdateWrapper
核心特性:专为更新操作设计的构造器,无需创建实体类对象,可直接在构造器中同时设置更新字段值和更新条件,比 QueryWrapper 做更新操作更简洁。
关键能力:支持set方法直接设置字段固定值,也支持setSql方法编写自定义 SQL 更新逻辑(如age = age+10这类字段自运算操作)。
java
@Test
void testUpdateWrapper1() {
UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("delete_flag", 0).set("age", 5).in("id", List.of(1, 13, 15));
userInfoMapper.update(updateWrapper);
}
java
@Test
void testUpdateWrapper2() {
UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.setSql("age = age + 10").in("id", List.of(1, 13, 15));
userInfoMapper.update(updateWrapper);
}
3. LambdaQueryWrapper
核心特性:基于Lambda 表达式的查询构造器,是 QueryWrapper 的优化版,通过引用实体类的属性(如UserInfo::getAge)替代硬编码字段名。
核心优势:彻底解决 QueryWrapper 中字段名硬编码的问题,若数据库字段名变更,实体类属性也会同步修改,编译器会直接检测到错误,避免因字段名修改未同步导致的程序问题,提升代码可读性和可维护性。
使用方式:在原有 QueryWrapper 对象上调用lambda()方法,即可切换为 Lambda 方式的条件构建。
java
@Test
void testLambdaQueryWrapper() {
QueryWrapper<Userinfo> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda()
.select(Userinfo::getUserId, Userinfo::getUserName, Userinfo::getPassword)
.eq(Userinfo::getAge, 15);
userInfoMapper.selectList(queryWrapper);
}
4. LambdaUpdateWrapper
核心特性:基于Lambda 表达式的更新构造器,对应 UpdateWrapper 的优化版,同样通过 Lambda 引用实体类属性,避免硬编码更新字段和条件字段名。
使用方式:与 LambdaQueryWrapper 一致,在 UpdateWrapper 对象上调用lambda()方法,通过实体类方法引用指定字段。
java
@Test
void testLambdaUpdateWrapper(){
UpdateWrapper<Userinfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().set(Userinfo::getDeleteFlag, 0)
.set(Userinfo::getAge,5).in(Userinfo::getId, List.of(1,13,15));
userInfoMapper.update(updateWrapper);
}