MyBatis Plus 封神玩法:这12个操作让开发效率直接起飞!
说起数据库ORM我忽然想起了小时候外婆做的那锅鲜美的羊肉汤。平常人家做的羊肉汤无非是几块肉、几片姜味道寡淡得很喝了和喝白开水差不多。但外婆的汤那是另一回事儿 —— 一锅汤香气四溢肉质软烂汤头浓郁得能让人连碗都想舔干净。写代码何尝不是如此以前写Mybatis就像是在煮一锅没有灵魂的羊肉汤原料都在但就是不够鲜美。代码繁琐每写一个查询都像是在不断调味却怎么也调不出那种令人惊艳的味道。直到遇见MyBatisPlus一切都变了 —— 这就像是从普通的羊肉汤突然升级到了外婆秘制的顶级羊肉汤MyBatisPlus就像一位精通厨艺的帮厨它帮你处理了所有繁琐的准备工作。想要一个复杂的查询不用自己一刀一刀地切肉、一勺一勺地调味框架已经帮你准备好了。你只需要轻轻地指挥代码就像汤汁一样顺滑流畅性能更是鲜美可口。在接下来的篇幅里我将与你分享12个MyBatisPlus优化的秘制配方。相信看完这些你写的每一行代码都会像外婆的羊肉汤一样让人回味无穷。耐心看完你一定有所收获。1. 避免使用isNull判断// ❌ 不推荐 LambdaQueryWrapperUser wrapper1 new LambdaQueryWrapper(); wrapper1.isNull(User::getStatus); // ✅ 推荐使用具体的默认值 LambdaQueryWrapperUser wrapper2 new LambdaQueryWrapper(); wrapper2.eq(User::getStatus, UserStatusEnum.INACTIVE.getCode()); 原因:使用具体的默认值可以提高代码的可读性和维护性NULL值会使索引失效导致MySQL无法使用索引进行查询优化NULL值的比较需要特殊的处理逻辑增加了CPU开销NULL值会占用额外的存储空间影响数据压缩效率2. 明确Select字段// ❌ 不推荐默认select 所有字段 ListUser users1 userMapper.selectList(null); // ✅ 推荐指定需要的字段 LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.select(User::getId, User::getName, User::getAge); ListUser users2 userMapper.selectList(wrapper); 原因:避免大量无用字段的网络传输开销可以利用索引覆盖避免回表查询减少数据库解析和序列化的负担降低内存占用特别是在大量数据查询时3. 批量操作方法替代循环// ❌ 不推荐 for (User user : userList) { userMapper.insert(user); } // ✅ 推荐 userService.saveBatch(userList, 100); // 每批次处理100条数据 // ✅ 更优写法自定义批次大小 userService.saveBatch(userList, BatchConstants.BATCH_SIZE); 原因:减少数据库连接的创建和销毁开销批量操作可以在一个事务中完成提高数据一致性数据库可以优化批量操作的执行计划显著减少网络往返次数提升吞吐量4. Exists方法子查询// ❌ 不推荐 wrapper.inSql(user_id, select user_id from order where amount 1000); // ✅ 推荐 wrapper.exists(select 1 from order where order.user_id user.id and amount 1000); // ✅ 更优写法使用LambdaQueryWrapper wrapper.exists(orderService.lambdaQuery() .gt(Order::getAmount, 1000) .apply(order.user_id user.id)); 原因:EXISTS是基于索引的快速查询可以使用到索引EXISTS在找到第一个匹配项就会停止扫描IN子查询需要加载所有数据到内存后再比较当外表数据量大时EXISTS的性能优势更明显5. 使用orderBy代替last拼接排序// ❌ 不推荐SQL注入风险 wrapper.last(ORDER BY sortField sortOrder); // ❌ 不推荐直接字符串拼接 wrapper.last(ORDER BY FIELD(status, active, pending, inactive)); // ✅ 推荐使用 Lambda 安全排序 wrapper.orderBy(true, true, User::getStatus); // ✅ 推荐多字段排序示例 wrapper.orderByAsc(User::getStatus) .orderByDesc(User::getCreateTime); 原因:直接拼接SQL容易导致SQL注入攻击动态SQL可能破坏SQL语义完整性影响SQL语句的可维护性和可读性last会绕过MyBatis-Plus的安全检查机制6. 使用LambdaQuery确保类型安全// ❌ 不推荐字段变更后可能遗漏 QueryWrapperUser wrapper1 new QueryWrapper(); wrapper1.eq(name, 张三).gt(age, 18); // ✅ 推荐 LambdaQueryWrapper LambdaQueryWrapperUser wrapper2 new LambdaQueryWrapper(); wrapper2.eq(User::getName, 张三) .gt(User::getAge, 18); // ✅ 更优写法使用链式调用 userService.lambdaQuery() .eq(User::getName, 张三) .gt(User::getAge, 18) .list(); 原因:编译期类型检查避免字段名拼写错误IDE可以提供更好的代码补全支持重构时能自动更新字段引用提高代码的可维护性和可读性7. 用between代替ge和le// ❌ 不推荐 wrapper.ge(User::getAge, 18) .le(User::getAge, 30); // ✅ 推荐 wrapper.between(User::getAge, 18, 30); // ✅ 更优写法条件动态判断 wrapper.between(ageStart ! null ageEnd ! null, User::getAge, ageStart, ageEnd); 原因:生成的SQL更简洁减少解析开销数据库优化器可以更好地处理范围查询代码更易读语义更清晰减少重复编写字段名的机会8. 排序字段注意索引// ❌ 不推荐lastLoginTime无索引 wrapper.orderByDesc(User::getLastLoginTime); // ✅ 推荐主键排序 wrapper.orderByDesc(User::getId); // ✅ 更优写法组合索引排序 wrapper.orderByDesc(User::getStatus) // status建立了索引 .orderByDesc(User::getId); // 主键排序 原因:索引天然具有排序特性可以避免额外的排序操作无索引排序会导致文件排序极大影响性能当数据量大时内存排序可能导致溢出利用索引排序可以实现流式读取9. 分页参数规范设置// ❌ 不推荐last直接限制一次性拉取大量数据 wrapper.last(limit 1000); // ✅ 推荐 PageUser page new Page(1, 10); userService.page(page, wrapper); // ✅ 更优写法带条件的分页查询 PageUser result userService.lambdaQuery() .eq(User::getStatus, active) .page(new Page(1, 10)); 原因:控制单次查询的数据量避免内存溢出提高首屏加载速度优化用户体验减少网络传输压力数据库资源利用更合理10. 条件构造优雅处理Null值// ❌ 不推荐大量if判断 if (StringUtils.isNotBlank(name)) { wrapper.eq(name, name); } if (age ! null) { wrapper.eq(age, age); } // ✅ 推荐 wrapper.eq(StringUtils.isNotBlank(name), User::getName, name) .eq(Objects.nonNull(age), User::getAge, age); // ✅ 更优写法结合业务场景 wrapper.eq(StringUtils.isNotBlank(name), User::getName, name) .eq(Objects.nonNull(age), User::getAge, age) .eq(User::getDeleted, false) // 默认查询未删除记录 .orderByDesc(User::getCreateTime); // 默认按创建时间倒序 原因:优雅处理空值避免无效条件减少代码中的if-else判断提高代码可读性防止生成冗余的SQL条件⚠️ 高级技巧篇11. 查询性能追踪// ❌ 不推荐手动计时代码冗余 public ListUser listUsers(QueryWrapperUser wrapper) { long startTime System.currentTimeMillis(); ListUser users userMapper.selectList(wrapper); long endTime System.currentTimeMillis(); log.info(查询耗时{}ms, (endTime - startTime)); return users; } // ✅ 推荐使用 Try-with-resources 自动计时 public ListUser listUsersWithPerfTrack(QueryWrapperUser wrapper) { try (PerfTracker.TimerContext ignored PerfTracker.start()) { return userMapper.selectList(wrapper); } } // 性能追踪工具类 Slf4j public class PerfTracker { private final long startTime; private final String methodName; private PerfTracker(String methodName) { this.startTime System.currentTimeMillis(); this.methodName methodName; } public static TimerContext start() { return new TimerContext(Thread.currentThread().getStackTrace()[2].getMethodName()); } public static class TimerContext implements AutoCloseable { private final PerfTracker tracker; private TimerContext(String methodName) { this.tracker new PerfTracker(methodName); } Override public void close() { long executeTime System.currentTimeMillis() - tracker.startTime; if (executeTime 500) { log.warn(慢查询告警方法 {} 耗时 {}ms, tracker.methodName, executeTime); } } } } 原因:业务代码和性能监控代码完全分离try-with-resources 即使发生异常close() 方法也会被调用确保一定会记录耗时不需要手动管理计时的开始和结束代码更加优雅12. 枚举类型映射// 定义枚举 public enum UserStatusEnum { NORMAL(1, 正常), DISABLED(0, 禁用); EnumValue // MyBatis-Plus注解 private final Integer code; private final String desc; } // 实体类自动映射 public class User { private UserStatusEnum status; } // 查询示例 userMapper.selectList( new LambdaQueryWrapperUser() .eq(User::getStatus, UserStatusEnum.NORMAL) ); 原因:类型安全自动处理数据库和枚举转换避免魔法值代码可读性更强补充拓展实用功能1. 自动逻辑删除// 实体字段标记 TableLogic private Integer deleted; // 查询自动过滤已删除数据 public ListUser getActiveUsers() { return userMapper.selectList(null); // 自动过滤deleted1的记录 } // 删除操作实际执行更新 userService.removeById(1L); 原因:数据不丢失支持数据恢复查询自动过滤已删除数据减少手动编写删除逻辑注意XML自定义SQL需要手动拼接deleted 02. 乐观锁并发更新保护// 实体添加版本字段 public class Product { Version // 乐观锁版本号 private Integer version; } // 更新时自动处理版本 public boolean reduceStock(Long productId, Integer count) { LambdaUpdateWrapperProduct wrapper new LambdaUpdateWrapper(); wrapper.eq(Product::getId, productId) .ge(Product::getStock, count); Product product new Product(); product.setStock(product.getStock() - count); return productService.update(product, wrapper); } 原因:防止并发冲突自动处理版本控制简化并发更新逻辑提高数据一致性3. 字段自增/自减 setIncrBy / setDecrBy// ❌ 不推荐手写setSql存在注入风险 userService.lambdaUpdate() .setSql(integral integral 10) .update(); // ✅ 推荐积分自增 userService.lambdaUpdate() .eq(User::getId, 1L) .setIncrBy(User::getIntegral, 10) .update(); // ✅ 推荐库存自减 userService.lambdaUpdate() .eq(User::getId, 1L) .setDecrBy(User::getStock, 5) .update(); 原因:类型安全编译校验避免手动拼接sql防止sql注入代码可维护性更强逻辑清晰总结写代码如烹小鲜讲究的是精细和用心。就像一碗好汤不仅仅在于锅和火候更在于厨师对食材的理解和尊重。MyBatisPlus的这12个优化技巧何尝不是程序员对代码的一种尊重和雕琢还记得文章开头说的外婆的羊肉汤吗优秀的代码和一碗好汤都需要用心。MyBatisPlus就像是厨房里的得力助手它帮你处理繁琐让你专注于创造。当你掌握了这些技巧你的代码将不再是简单的指令堆砌而是一首优雅的诗一曲悦耳的交响乐。它们将像外婆的羊肉汤一样散发着独特的魅力让人回味无穷。愿每一位开发者都能用MyBatisPlus煮出属于自己的秘制汤羹代码就应该是这个样子 —— 简单而不失优雅高效而不失温度。

相关新闻

MockServer:把 mock 和 proxy 揉到一个端口里

MockServer:把 mock 和 proxy 揉到一个端口里

文章目录MockServer:把 mock 和 proxy 揉到一个端口里1、 它能干嘛2、 协议覆盖到什么程度3、 60 秒上手4、 客户端和集成5、 怎么跑6、 一些硬性要求7、 适合谁MockServer:把 mock 和 proxy 揉到一个端口里 mockserver-monorepo 在 GitHub 上拿到 4,91…

2026/7/9 8:20:19阅读更多 →
企业级远程桌面解决方案:TigerVNC跨平台架构设计与信创环境深度适配

企业级远程桌面解决方案:TigerVNC跨平台架构设计与信创环境深度适配

企业级远程桌面解决方案:TigerVNC跨平台架构设计与信创环境深度适配 【免费下载链接】tigervnc High performance, multi-platform VNC client and server 项目地址: https://gitcode.com/gh_mirrors/ti/tigervnc TigerVNC作为一款高性能、多平台的VNC客户端…

2026/7/9 8:20:19阅读更多 →
检查一下编译产物:

检查一下编译产物:

Test-Path dist/main.js # 或者 dist/src/main.js(看你的配置)如果返回 True,说明编译成功了。💡 实在不行,你也可以在本地编译好了,把整个 dist 文件夹直接上传到服务器,跳过服务器上编译这一…

2026/7/9 8:20:19阅读更多 →
MyComputerManager终极指南:如何彻底清理Windows“此电脑“中的顽固图标

MyComputerManager终极指南:如何彻底清理Windows“此电脑“中的顽固图标

MyComputerManager终极指南:如何彻底清理Windows"此电脑"中的顽固图标 【免费下载链接】MyComputerManager 管理“此电脑”里删不掉的流氓“快捷方式”(包括侧边栏),同时可自己添加这类“快捷方式” 项目地址: https:…

2026/7/9 9:20:31阅读更多 →
pandas 内存优化:read_csv 的 dtype 参数能省一半内存

pandas 内存优化:read_csv 的 dtype 参数能省一半内存

pandas 内存优化:read_csv 的 dtype 参数能省一半内存公司那台 16G 内存的开发机,被一个 800M 的 CSV 文件干到 OOM。排查了一圈才发现,pandas 默认推断类型这件事,偷偷多吃了一倍的内存。今天用实际案例聊聊 dtype 参数怎么给数据…

2026/7/9 9:20:31阅读更多 →
ESLint 自定义规则实战:用 AST 分析封堵团队特有的代码坏味道

ESLint 自定义规则实战:用 AST 分析封堵团队特有的代码坏味道

ESLint 自定义规则实战:用 AST 分析封堵团队特有的代码坏味道 一、通用 Lint 规则的盲区:为什么团队特有的"坏习惯"永远被漏掉 ESLint 有数百条内置规则和社区插件。no-unused-vars、no-console、prefer-const 这些通用规则覆盖了 80% 的常见…

2026/7/9 9:20:29阅读更多 →
ARIMA模型实战:Python statsmodels 0.14 电商销量预测,MAPE误差降至8%

ARIMA模型实战:Python statsmodels 0.14 电商销量预测,MAPE误差降至8%

ARIMA模型实战:Python statsmodels 0.14 电商销量预测,MAPE误差降至8% 1. 电商销量预测的业务挑战与ARIMA解决方案 在电商运营中,精准的销量预测是库存管理、营销策略和供应链优化的核心。传统方法如移动平均或简单指数平滑往往难以捕捉销售…

2026/7/9 9:20:29阅读更多 →
web应用课程设计最终项目展示

web应用课程设计最终项目展示

云边书屋|基于Spring BootVue3的前后端分离图书管理系统一、项目简介 云边书屋是一款Spring Boot Vue3开发的前后端分离在线书库管理系统,项目寓意“于云端书海之畔,打造极简治愈的线上阅读空间”。系统兼具用户阅读体验与后台管理能力&…

2026/7/9 9:20:28阅读更多 →
京东app 滑块验证码逆向分析

京东app 滑块验证码逆向分析

声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!有相关问题请第一时间点击头像看简介或私…

2026/7/9 9:15:27阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/9 5:56:19阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/8 7:00:12阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/9 2:47:07阅读更多 →
Three.js 着色器光效教程

Three.js 着色器光效教程

着色器光效 Shader Light ▶ 在线运行案例 案例合集: 三维可视化功能案例(threehub.cn)开源仓库github地址: https://github.com/z2586300277/three-cesium-examples400个案例代码: 网盘链接 你将学到什么 ShaderMaterial 自…

2026/7/9 0:04:37阅读更多 →
如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南

如何5分钟掌握CS2智能库存管理:开源工具CASEMOVE终极指南 【免费下载链接】casemove A dedicated desktop app that enables you to move items in and out of storage units in CS2. 项目地址: https://gitcode.com/gh_mirrors/ca/casemove 还在为CS2存储单…

2026/7/9 0:04:37阅读更多 →
GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a vs MySQL 8.0:ALTER TABLE语法与限制的5点关键差异对比

GBase 8a与MySQL 8.0:ALTER TABLE语法差异深度解析与实战指南1. 两种数据库的ALTER TABLE能力全景对比在数据库架构设计和运维过程中,表结构变更(DDL操作)是不可避免的需求。GBase 8a作为国产分析型数据库代表,与开源M…

2026/7/9 0:04:37阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/8 6:59:54阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/8 13:42:39阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/8 13:42:39阅读更多 →