当前位置: 首页 > java >正文

【MyBatis-Plus】核心开发指南:高效CRUD与进阶实践


目录

1.前言

插播一条消息~

2.正文

2.1介绍

2.2基础操作

2.2.1准备工作

2.2.2编码

2.2.3CRUD测试

2.3复杂操作

2.3.1常见注解

2.3.1.1@TableName

2.3.1.2@TableField

2.3.1.3@TableId

2.3.2打印日志

2.3.3条件构造器

2.3.3.1QueryWrapper

2.3.3.2UpdateWrapper

2.3.3.3LambdaQueryWrapper

2.3.3.4LambdaUpdateWrapper

2.3.4自定义SQL

2.3.4.1注解方式

2.3.4.2XML映射

核心技巧:Wrapper条件集成

2.3.4.3常见故障

3.小结


1.前言

在传统MyBatis开发中,我们常陷入重复劳动:

  • 每张表需手动编写基础CRUD的Mapper接口与XML
  • 复杂查询需拼接SQL字符串,易引发语法错误与SQL注入风险
  • 分页、乐观锁等通用功能需重复造轮子

MyBatis-Plus(MP)的诞生直击上述痛点。作为MyBatis的增强工具包,它在保留MyBatis所有灵活性的基础上,通过两大核心设计大幅提升开发效率:

  1. 内置通用Mapper/Service:自动化单表CRUD操作,减少70%重复代码
  2. 链式条件构造器:用类型安全的Java API替代SQL字符串拼接
  3. 插件化架构:分页、性能分析、乐观锁等企业级功能开箱即用

本文将系统解析MP的核心工作机制,涵盖以下重点:

  • 基础CRUD的零SQL实现
  • 注解驱动的表字段映射策略
  • 基于Lambda的条件构造器安全写法
  • 自定义SQL与MP特性的无缝融合

插播一条消息~

🔍十年经验淬炼 · 系统化AI学习平台推荐

系统化学习AI平台https://www.captainbed.cn/scy/

📚 完整知识体系:从数学基础 → 工业级项目(人脸识别/自动驾驶/GANs),内容由浅入深
💻 实战为王:每小节配套可运行代码案例(提供完整源码)
🎯 零基础友好:用生活案例讲解算法,无需担心数学/编程基础

🚀 特别适合

  • 想系统补强AI知识的开发者
  • 转型人工智能领域的从业者
  • 需要项目经验的学生

2.正文

2.1介绍

什么是MyBatis Plus呢?

简介 | MyBatis-Plushttps://baomidou.com/introduce/

一句话:在MyBatis基础上只做增强不做改变,简化开发的“效率神器”
它保留了MyBatis的全部特性,同时内置了CRUD接口、分页插件、代码生成器等实用功能,让你告别重复写SQL的痛苦。

2.2基础操作

2.2.1准备工作

核心依赖配置

<!-- Spring Boot Starter -->
<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><version>8.0.33</version>
</dependency>

配置文件(application.yml)

spring:datasource:url: jdbc:mysql://localhost:3306/mp_demo?useSSL=false&serverTimezone=UTCusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivermybatis-plus:configuration:# 关键:开启SQL日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:# 全局表前缀table-prefix: t_

实体类定义

@Data
@TableName("user")  // 指定对应数据库表
public class User {@TableId(type = IdType.AUTO)  // 主键自增策略private Long id;private String name;private Integer age;@TableField("user_email")  // 映射字段名private String email;
}

 Mapper接口定义

@Mapper
public interface UserMapper extends BaseMapper<User> {// 无需任何方法声明// 已自动继承17个基础CRUD方法
}

2.2.2编码

核心CRUD方法

@Service
public class UserService {@Autowiredprivate UserMapper userMapper;// 插入public int createUser(User user) {return userMapper.insert(user);// 注意:自增ID会自动回填到user对象}// 按ID查询public User getUserById(Long id) {return userMapper.selectById(id);}// 更新public int updateUser(User user) {return userMapper.updateById(user);}// 删除public int deleteUser(Long id) {return userMapper.deleteById(id);}// 查询所有public List<User> getAllUsers() {return userMapper.selectList(null);  // null表示无条件}
}

2.2.3CRUD测试

测试场景设计

@SpringBootTest
class UserCrudTests {@Autowiredprivate UserService userService;@Testvoid testFullCrudFlow() {// 1. 创建测试数据User newUser = new User();newUser.setName("TestUser");newUser.setAge(30);newUser.setEmail("test@mp.com");// 2. 插入测试(验证日志输出)int createResult = userService.createUser(newUser);assertEquals(1, createResult);  // 影响行数=1assertNotNull(newUser.getId());  // 自增ID回填验证// 3. 查询验证User dbUser = userService.getUserById(newUser.getId());assertEquals("TestUser", dbUser.getName());// 4. 更新测试dbUser.setEmail("updated@mp.com");int updateResult = userService.updateUser(dbUser);assertEquals(1, updateResult);// 5. 二次查询验证更新User updatedUser = userService.getUserById(dbUser.getId());assertEquals("updated@mp.com", updatedUser.getEmail());// 6. 删除测试int deleteResult = userService.deleteUser(updatedUser.getId());assertEquals(1, deleteResult);// 7. 验证删除结果User deletedUser = userService.getUserById(updatedUser.getId());assertNull(deletedUser);}
}

关键日志分析(控制台输出):

技术要点总结

  1. 零SQL实现:所有基础CRUD操作无需编写SQL语句

  2. 自动映射

    • 表名映射:@TableName 或全局配置

    • 字段映射:自动驼峰转下划线(userEmail → user_email

    • 主键策略:通过@TableId指定(自增/雪花算法等)

  3. 方法继承

    // BaseMapper提供的基础方法
    int insert(T entity);
    int deleteById(Serializable id);
    int updateById(T entity);
    T selectById(Serializable id);
    List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
  4. 事务支持:默认继承Spring事务管理

  5. 自动填充:插入/更新时自动填充create_time等字段(需配合@TableField


2.3复杂操作

2.3.1常见注解

2.3.1.1@TableName

作用:建立实体类与数据库表的映射关系
使用场景

  • 表名与类名不一致时(如类名User对应表名sys_user

  • 需要动态表名(如分表场景)

  • 需要指定数据库schema

核心参数

@TableName(value = "sys_user",          // 实际表名schema = "enterprise_db",    // 数据库schemakeepGlobalPrefix = true      // 是否使用全局表前缀
)
public class User {// ...
}

 配置示例

# application.yml
mybatis-plus:global-config:db-config:table-prefix: "tbl_"    # 全局表前缀table-underline: true    # 开启下划线命名转换

使用规则

  1. 优先使用注解中的value

  2. 未配置value时,自动转换类名(User → user)

  3. 开启keepGlobalPrefix时,表名 = 全局前缀 + value值


2.3.1.2@TableField

作用:建立属性与表字段的映射关系
核心参数

public class User {@TableField(value = "full_name",            // 数据库字段名exist = true,                   // 是否为表字段(false表示虚拟字段)insertStrategy = FieldStrategy.NOT_NULL,  // 插入策略updateStrategy = FieldStrategy.IGNORED,   // 更新策略fill = FieldFill.INSERT_UPDATE  // 自动填充策略)private String name;
}

字段策略(FieldStrategy)

策略说明使用场景
IGNORED忽略空值判断强制更新字段(包括null值)
NOT_NULL非NULL才更新/作为条件默认策略
NOT_EMPTY非空才更新(对字符串有效)避免空字符串覆盖
NEVER从不更新只读字段

自动填充(FieldFill)

// 自动填充处理器
@Component
public class AutoFillHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}// 实体类配置
public class User {@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}

2.3.1.3@TableId

作用:标识主键字段并指定主键策略
核心参数

public class User {@TableId(value = "user_id",          // 主键字段名type = IdType.ASSIGN_ID     // 主键生成策略)private Long id;
}

主键策略(IdType)

策略说明适用场景
AUTO数据库自增MySQL、PostgreSQL
INPUT用户手动输入业务主键
ASSIGN_ID分配ID(雪花算法)分布式系统(默认策略)
ASSIGN_UUID分配UUID需要字符串主键
NONE无状态需手动设置

雪花算法配置

mybatis-plus:global-config:db-config:id-type: assign_idworker-id: 1      # 工作机器ID(0-31)datacenter-id: 1  # 数据中心ID(0-31)

注解应用综合示例

@TableName(value = "sys_users", schema = "hr_db")
public class User {@TableId(type = IdType.ASSIGN_ID)private Long id;@TableField(value = "full_name", updateStrategy = FieldStrategy.NOT_EMPTY)private String name;@TableField(exist = false)  // 非数据库字段private String tempToken;@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(value = "is_deleted",insertStrategy = FieldStrategy.NEVER,  // 插入时不包含updateStrategy = FieldStrategy.NEVER   // 更新时不包含)private Boolean deleted;
}

关键提示

  1. 使用@TableField(exist=false)标记DTO扩展字段

  2. 敏感字段设置updateStrategy=FieldStrategy.NEVER防止意外更新

  3. 主键策略必须与实际数据库结构匹配(自增字段需配置AUTO

  4. 开启SQL日志验证注解是否生效


2.3.2打印日志

核心价值:

  • SQL审计 → 验证自动生成的SQL是否符合预期
  • 性能优化 → 识别慢查询和N+1问题
  • 故障排查 → 定位参数绑定错误和语法异常

配置方式

基础配置(application.yml):

mybatis-plus:configuration:# 标准输出(控制台)log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# SLF4J+Logback(生产推荐)# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

 日志级别控制

# Logback配置(src/main/resources/logback.xml)
<logger name="com.baomidou.mybatisplus" level="DEBUG"/>
<logger name="org.apache.ibatis" level="TRACE"/>  # 查看参数细节

日志格式解析

典型日志输出

字段说明

日志行含义技术细节
Preparing:待执行SQL显示预编译语句(?占位符)
Parameters:绑定参数按顺序显示参数值和类型
Total:查询结果行数仅select语句显示
Updates:DML操作影响行数insert/update/delete操作
Time: 23ms执行耗时(需额外配置)性能分析关键指标

日志级别策略:

环境推荐配置说明
开发环境StdOutImpl + TRACE级别实时查看完整SQL
测试环境Slf4jImpl + DEBUG级别记录所有SQL但不输出参数值
生产环境Slf4jImpl + WARN级别仅记录慢查询和异常SQL

2.3.3条件构造器

核心架构

// 条件构造器类继承体系
AbstractWrapper├── QueryWrapper      // 查询条件构造├── UpdateWrapper     // 更新条件构造├── LambdaQueryWrapper // Lambda安全查询└── LambdaUpdateWrapper // Lambda安全更新
2.3.3.1QueryWrapper

核心功能:构建SELECT查询条件
适用场景:复杂条件查询、排序、分组

基础用法

QueryWrapper<User> query = new QueryWrapper<>();
query.select("id", "name", "age")  // 指定查询列.like("name", "张")           // 模糊查询.gt("age", 20)                // 大于.between("create_time", startDate, endDate)  // 范围查询.orderByDesc("id")            // 排序.last("LIMIT 10");            // 自定义SQL片段List<User> users = userMapper.selectList(query);

链式条件方法

方法SQL等价说明
eq("column", val)column = val等于
ne("column", val)column <> val不等于
gt("column", val)column > val大于
ge("column", val)column >= val大于等于
lt("column", val)column < val小于
le("column", val)column <= val小于等于
between(c, v1, v2)c BETWEEN v1 AND v2范围查询
like("c", "val%")c LIKE 'val%'模糊查询
in("c", list)c IN (v1,v2)IN查询
isNull("c")c IS NULL空值判断
groupBy("c1,c2")GROUP BY c1,c2分组
having("sum(a)>0")HAVING sum(a)>0分组后条件

2.3.3.2UpdateWrapper

核心功能:构建UPDATE操作条件
适用场景:条件更新、字段增量操作

基础用法

UpdateWrapper<User> update = new UpdateWrapper<>();
update.set("email", "new@email.com")  // 设置更新值.setSql("balance = balance + 100")  // 原生SQL更新.eq("status", 1)                // 更新条件.gt("last_login", LocalDate.now().minusMonths(3)); // 三个月内有登录int rows = userMapper.update(null, update);  // entity传null

特殊操作符

// 数学运算
update.setSql("view_count = view_count + 1");// JSON字段更新 (MySQL 5.7+)
update.setSql("profile = JSON_SET(profile, '$.level', 2)");// CASE WHEN更新
update.setSql("vip_level = CASE WHEN score>1000 THEN 2 ELSE 1 END");

2.3.3.3LambdaQueryWrapper

核心价值:消除字段名字符串硬编码
编译时检查:字段引用通过方法引用实现

基础用法

LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
lambdaQuery.select(User::getId, User::getName).like(User::getName, "王").between(User::getAge, 20, 30).orderByDesc(User::getCreateTime);List<User> users = userMapper.selectList(lambdaQuery);

复杂嵌套查询

lambdaQuery.and(wrapper -> wrapper.gt(User::getScore, 90).or().eq(User::getVipLevel, 2)).nested(wrapper -> wrapper.lt(User::getAge, 18).or().gt(User::getAge, 60));

等价SQL

SELECT id, name 
FROM user 
WHERE (score > 90 OR vip_level = 2)AND (age < 18 OR age > 60)

2.3.3.4LambdaUpdateWrapper

核心价值:类型安全的更新操作

基础用法

LambdaUpdateWrapper<User> lambdaUpdate = new LambdaUpdateWrapper<>();
lambdaUpdate.set(User::getEmail, "updated@email.com").set(User::getUpdateTime, LocalDateTime.now()).eq(User::getDepartmentId, 101).in(User::getRole, Arrays.asList("admin", "editor"));int rows = userMapper.update(null, lambdaUpdate);

条件更新技巧

// 按条件设置不同值
lambdaUpdate.setSql("score = CASE " +"WHEN age < 30 THEN score + 10 " +"WHEN age > 50 THEN score + 5 " +"ELSE score + 8 END").le(User::getScore, 100);

四类构造器对比矩阵

特性QueryWrapperUpdateWrapperLambdaQueryWrapperLambdaUpdateWrapper
核心用途SELECT条件UPDATE条件类型安全SELECT类型安全UPDATE
字段引用方式字符串列名字符串列名方法引用(::)方法引用(::)
编译时检查
重构友好度
复杂嵌套支持
SQL函数支持
推荐指数⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

2.3.4自定义SQL

解决MP的局限性场景:

  • 多表关联查询(JOIN操作)
  • 复杂聚合函数(SUM/COUNT嵌套)
  • 数据库特性函数(如Oracle的CONNECT BY)
  • 存储过程/函数调用
  • 特殊分页需求(优化count查询)

实现方式对比

方式适用场景优势劣势
注解SQL简单动态SQL零配置快速实现复杂SQL可读性差
XML映射复杂SQL/多表操作完整SQL语法支持需额外配置文件
Wrapper+条件复用场景动态条件与固定SQL结合仅限WHERE/ORDER BY

2.3.4.1注解方式

基础语法

@Select("SELECT * FROM user WHERE age > #{minAge}")
List<User> findAdultUsers(@Param("minAge") int minAge);

动态条件注解

@Select("<script>" +"SELECT * FROM user " +"WHERE 1=1 " +"<if test='name != null'> AND name LIKE #{name} </if>" +"<if test='minAge != null'> AND age >= #{minAge} </if>" +"</script>")
List<User> findUsersDynamic(@Param("name") String name, @Param("minAge") Integer minAge
);

多表关联示例

@Select("SELECT u.*, d.dept_name " +"FROM user u " +"LEFT JOIN department d ON u.dept_id = d.id " +"WHERE u.status = 1")
List<Map<String, Object>> findUsersWithDept();

限制

  1. 不支持${ew.customSqlSegment}注入

  2. 复杂SQL可维护性差


2.3.4.2XML映射

基础配置

<!-- 启用XML扫描 -->
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xml
核心技巧:Wrapper条件集成

Mapper接口定义

public interface UserMapper extends BaseMapper<User> {// 使用@Param("ew")固定命名List<UserDTO> selectComplexUsers(@Param("ew") Wrapper<User> wrapper);
}

XML映射实现

<select id="selectComplexUsers" resultType="com.example.dto.UserDTO">SELECT u.id, u.name,d.dept_name AS deptName,COUNT(o.id) AS orderCountFROM user uLEFT JOIN department d ON u.dept_id = d.idLEFT JOIN orders o ON o.user_id = u.id<!-- 关键:集成Wrapper生成的条件 -->${ew.customSqlSegment}GROUP BY u.id
</select>

Java调用示例

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张").gt(User::getAge, 25).orderByDesc(User::getCreateTime);List<UserDTO> users = userMapper.selectComplexUsers(wrapper);

生成SQL

SELECT u.id, u.name, d.dept_name, COUNT(o.id)
FROM user u
LEFT JOIN department d ON u.dept_id = d.id
LEFT JOIN orders o ON o.user_id = u.id
WHERE name LIKE '%张%' AND age > 25  -- 自动生成的条件
GROUP BY u.id
ORDER BY create_time DESC           -- 自动排序

2.3.4.3常见故障
问题现象原因分析解决方案
ew.customSqlSegment无效未使用@Param("ew")检查Mapper参数命名
分页总数错误未重写count查询实现selectPageCount方法
字段映射失败缺少@ResultMap配置显式定义resultMap
动态SQL解析异常XML标签未闭合检查<script>完整性
存储过程调用失败未设置statementType添加statementType="CALLABLE"

通过合理选择自定义SQL方案,可在保持MP高效开发的同时,应对各类复杂数据库操作场景。 


3.小结

通过本文的系统讲解,我们完整梳理了MyBatis Plus的核心功能体系。从环境搭建到复杂查询,从自动映射机制到条件构造器的深度应用,每个技术点都提供了可落地的解决方案。

  • 开发效率提升:通用Mapper消灭单表CRUD代码
  • 维护成本降低:Lambda Wrapper杜绝SQL拼接错误
  • 架构统一性:注解体系提供标准化映射方案
特性最佳实践避坑指南
基础CRUD优先继承BaseMapper主键策略需显式声明(@TableId)
字段映射非常规字段必用@TableField非表字段标记exist=false
条件构造器强制使用Lambda Wrapper避免字段名字符串硬编码
自定义SQL结合${ew.customSqlSegment}XML中防SQL注入过滤

开发建议:MP适用于80%的单表操作场景,剩余20%复杂查询通过自定义SQL+Wrapper解决。掌握此平衡点,可最大化开发效能。

建议开发者在实际项目中逐步引入MyBatis Plus的特性,从基础CRUD开始,逐步过渡到复杂查询和分页处理。通过本文提供的代码示例模板,可以快速构建标准的数据库访问层,最终实现"代码精简、开发效率提升"的优化目标。

http://www.xdnf.cn/news/16085.html

相关文章:

  • 83、设置有人DTU设备USR-M100采集传感器数据,然后上传阿里云服务
  • 【音视频学习】五、深入解析视频技术中的像素格式:颜色空间、位深度、存储布局
  • CodeBuddy IDE实战:用AI全栈能力快速搭建课程表网页
  • 借助Aspose.HTML控件,使用 Python 编程将网页转换为 PDF
  • Object Sense (OSE):一款从编辑器脚本发展起来的编程语言
  • 优化:Toc小程序猜你喜欢功能
  • Java 堆(优先级队列)
  • AI 及开发领域动态与资源汇总(2025年7月23日)
  • 编程语言Java——核心技术篇(二)类的高级特性
  • 逆向入门(41)程序逆向篇-crackme
  • OceanBase数据库
  • 设备虚拟化技术
  • 从零开始学习Dify-Excel数据可视化(四)
  • Rocky9部署Zabbix7(小白的“升级打怪”成长之路)
  • 【bug】websocket协议不兼容导致的一个奇怪问题
  • (46)elasticsearch-华为云CCE无状态负载部署
  • #Linux内存管理# 在一个播放系统中同时打开几十个不同的高清视频文件,发现播放有些卡顿,打开视频文件是用mmap函数,请简单分析原因。
  • MCU芯片AS32S601在卫星光纤放大器(EDFA)中的应用探索
  • VPS海外部署Linux分布式计算任务调度-跨国资源整合方案
  • k8s:docker compose离线部署haborV2.13.1及采用外部的postgresql及redis数据库
  • uni-app动态获取屏幕边界到安全区域距离的完整教程
  • 在离线 Ubuntu 22.04机器上运行 ddkj_portainer-cn 镜像 其他相关操作也可以复刻 docker
  • Elasticsearch 学习笔记
  • 使用react编写一个简单的井字棋游戏
  • nodejs模块化
  • JS WebAPIs DOM节点概述
  • 前端_Javascript复习
  • C语言:第11天笔记
  • Python通关秘籍(四)数据结构——列表
  • 力扣 hot100 Day52