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

SpringData JPA也能写sql,为什么还要用mybatis?

因为在中大型系统里,可控省事重要的多。MyBatis 的核心价值不是能写SQL,而是它在:开发效率、SQL可控、性能底线,给了一个最稳的平衡点

我们作为程序员,日常在做技术架构设计时,也是一个寻找最佳平衡的过程,很少有绝对完美的解决方案的,选择最佳平衡最合适当前实际情况的就可以了。

网络上也有部分人拿Spring Data JPA 也能写原生 SQL来反驳:那 JPA 不就同时拥有便利 + SQL 灵活了吗?

真正负责过中大型系统就会知道:能写SQL ≠ 适合把SQL当成主战场。尤其当你的系统进入“数据量大、查询复杂、DBA开始介入治理”的阶段。


数据库是瓶颈:SQL 必须“可解释、可控、可审”

在日常的业务开发中,如果你做的多了,经历的项目也多了,会发现,好多系统的瓶颈就出在数据库。我相信经验丰富的人,不会反驳这一点。当生产环境里的业务系统接口的响应变慢了,定位下来后,好多都是跟SQL相关的,也经常会如下去处理:

  • SQL 长什么样?走了哪个索引?
  • 扫了多少行?有没有回表?
  • join 顺序对不对?分页对不对?
  • where 条件有没有不标准的写法导致索引失效?
  • 锁范围有多大?会不会把别人卡死?

我们要写好SQL尚且是一个难题,那直接让ORM去生成SQL,则更加不靠谱。

MyBatis的优势之一:所见即所得。
你写下的 SQL 就是最终执行的 SQL,DBA 看得懂,研发也能 explain、能优化、能对比。

而 JPA(Hibernate)的设计目标恰好相反:
它希望你从“对象关系”出发,把 SQL 的细节藏起来。

这在简单 CRUD 上确实省事,但在高并发/大数据量场景下,你会遇到两个典型坑:

  • 不可预期的 SQL:关联关系、懒加载、级联、flush 时机,一变就可能多出几条 SQL
  • 排查链路长:慢了之后你不是直接改 SQL,而是在改实体映射策略、Fetch、关联关系,牵一发而动全身

因此:

MyBatis更像是“把数据库当核心资产来治理”的,SQL是它的一等公民


JPA支持原生SQL,但复杂结果映射支持的不是很友好

JPA 的确可以@Query(nativeQuery = true)写 SQL。 但有一个问题,复杂SQL的结果如何很好的映射回到Java的对象里?

举一个例子:查用户列表 + 部门名 + 最近一年订单总额。

JPA(原生 SQL 的常见落地形态)

@Query(value=""" SELECT u.id, u.name, d.dept_name, SUM(o.amount) total_amount FROM t_user u JOIN t_dept d ON d.id = u.dept_id LEFT JOIN t_order o ON o.user_id = u.id AND o.created_at >= :since GROUP BY u.id, u.name, d.dept_name """,nativeQuery=true)List<Object[]>userReport(@Param("since")LocalDateTimesince);

接下来你要做的是:

  • 处理Object[],手动转 DTO;
  • 用 Projection(字段名对齐、类型对齐等);
  • 用构造函数表达式。

一旦字段多、join 多、统计多,映射逻辑会越来越多。

MyBatis(结果集映射是强项)

MyBatis 的ResultMap/resultType天然就是为这种场景准备的:
你只需要明确“列 -> 字段”的映射关系。

<resultMapid="UserReportMap"type="com.example.dto.UserReportDTO"><idproperty="userId"column="id"/><resultproperty="userName"column="name"/><resultproperty="deptName"column="dept_name"/><resultproperty="totalAmount"column="total_amount"/></resultMap><selectid="userReport"resultMap="UserReportMap">SELECT u.id, u.name, d.dept_name, SUM(o.amount) AS total_amount FROM t_user u JOIN t_dept d ON d.id = u.dept_id LEFT JOIN t_order o ON o.user_id = u.id AND o.created_at >= #{since} GROUP BY u.id, u.name, d.dept_name</select>

这里也可能有人会反驳说,JPA不是有@SqlResultSetMapping和接口投影技术来解决这个问题吗? 算数了,我为了一个映射,还得引入那么多东西,复杂性和维护性都提高了。

JPA写复杂SQL不是不能写,是写完之后落地成本高,MyBatis是把复杂查询 + 映射当主场来设计的。


动态 SQL:这是MyBatis的杀手锏,也是JPA的痛点

我们再来一个需求:用户列表搜索接口,10 个查询条件,全是可选。

  • name 模糊
  • status
  • phone
  • createdAt 范围
  • ids in (…)
  • deptId/ 渠道来源 / 注册方式……
  • 还要分页、排序

在JPA可以怎么做?

  • 拼字符串:可读性差、容易出错,也容易留下安全隐患
  • Specification/Criteria API:能写,但代码很绕,维护成本高(很多项目写着写着就开始受不了”)
  • 引入 QueryDSL:能解决一部分,但又引入一套新体系,学习/维护成本并不低

MyBatis 的写法(直观、可维护)

<selectid="searchUsers"resultType="User">SELECT id, name, phone, status, created_at FROM t_user<where><iftest="name != null and name != ''">AND name LIKE CONCAT('%', #{name}, '%')</if><iftest="status != null">AND status = #{status}</if><iftest="ids != null and ids.size > 0">AND id IN<foreachcollection="ids"item="id"open="("separator=","close=")">#{id}</foreach></if></where>ORDER BY id DESC LIMIT #{offset}, #{pageSize}</select>

你能一眼看明白:

  • 条件怎么拼出来的
  • SQL 最终长什么样
  • 该加什么索引、怎么解释执行计划

这也是很多团队最后的选择:

业务查询的复杂度,最终就是SQL条件组合的复杂度,MyBatis支持的很好,而不是让你写一堆框架语法,到处绕。


半自动ORM,有性能有效率

有人可能觉得MyBatis还要写 XML,太原始了,这个有一定的道理,因此后续大家也都引入了 MyBatis-Plus。

  • 简单 CRUD:MyBatis-Plus直接封装,效率不输JPA;
  • 复杂查询:手写 SQL,索引、join、分页、锁范围都能控制;

mybatis它允许你在简单场景下省事,在关键场景下不失控,这才是我们要的解决方案


线上问题排查的方便性

用Mybatis,线上出问题时,你需要的是:

  • 慢 SQL 能不能快速定位?
  • SQL 能不能快速复现并 explain?
  • 改动是不是可控?影响面是不是明确?
  • 能不能快速验证新索引、新 SQL 的收益?

MyBatis的路径很短:SQL 就在你手里。好排查、好治理。

如果你用是JPA,我真不知道如何快速处理,肯定是手忙脚乱的。


小结:为什么很多中大型团队最终更偏 MyBatis

  1. 性能底线:SQL 可控、可 explain、DBA 可审核;
  2. 排查效率:线上慢了能快速定位到具体 SQL;
  3. 复杂查询能力:join/聚合/报表/分页/动态条件是主场;
  4. 结果映射强:复杂结果集映射到 DTO 更顺手;
  5. 效率不吃亏:MyBatis-Plus还能让简单CRUD也很快。

最近在知乎出了

  • 「应付6000万会员的秒杀系统专栏」
  • 「几亿用户,百万并发的C端商品系统实战」
  • 「技术团队DDD领域驱动设计三年落地实战」
  • 「应付亿级用户规模的支付系统代码实战」
  • 「应付亿级用户的会员体系代码实战」

专栏,感兴趣的可以订阅一下。至于知识星球的,可以搜:

  • 老码头的技术浮生录

它是一个能实际帮你解决难题的星球。有问题的,找知心的Sam哥,支持无限次语音一对一解决你遇到的难题。「另外后续我新写的所有对外的付费专栏,在星球内都是免费的,且可以拿到所有源代码。」

当前星球里免费看的专栏是:

  • 「应付6000万会员的秒杀系统专栏」
  • 「几亿用户,百万并发的C端商品系统实战」
  • 「技术团队DDD领域驱动设计三年落地实战」
  • 「应付亿级用户规模的支付系统代码实战」
  • 「应付亿级用户的会员体系代码实战」

知识星球内后续将推出20+个付费专栏,覆盖电商全链路:

选购线用户会员营销线中后台
购物车服务营销系统订单系统
商品服务用户系统支付系统
菜单服务结算服务

从前台选购到中后台结算,星球成员全部免费,后续新增也不额外收费。

我的知乎账号:

  • SamDeepThinking
http://www.jsqmd.com/news/984627/

相关文章:

  • 物理层的FPGA实现的思考总结(1)
  • Paperxie 工科攻坚利器:AI 代码生成一键搞定毕业论文程序源码难题
  • 防眩光AG+硬化复合板厂家推荐:复合功能板适合哪些应用场景
  • 番禺洛浦奢侈品回收第一名|金小福名表名包名酒钻石翡翠黄金全品类专业回收 - 花生花生1
  • PyMuPDF:这个 Python 库,把 PDF 所有操作都覆盖了
  • 苹果WWDC26引爆全端AI产品,Meta/WIMI微美全息加速抢滩XR眼镜硬件市场
  • BiliBili-UWP桌面版终极秘籍:告别卡顿,打造你的专属B站体验
  • 2026年AI问答流量服务公司选购指南:技术架构、行业应用与决策框架 - 优质品牌商家
  • LumeValley|企业级Agent全栈开发,AI智能体规模化落地
  • 2026必看!独立开发者高性价比AI编程工具大全
  • Boss-Key:Windows用户的隐私守护神,一键隐藏窗口的终极解决方案
  • 2026 主流 GEO 源码厂商实测:云罗 GEO、摘星智能、棋引科技技术与落地能力对比
  • Effective C++ 条款06:若不想使用编译器自动生成的函数,就应该明确拒绝
  • 重新定义音乐自由:插件化播放器如何让你真正掌控音乐体验
  • 抗垢水路:SEGE在硬水地区保持清爽
  • idea+git插件+云备份实现项目新分支新建维护
  • 视觉伺服:基于图像的IBVS与基于位置的PBVS
  • 如何让《Honey Select 2》游戏体验全面升级:HS2-HF_Patch终极指南
  • Whisky终极指南:在macOS上轻松运行Windows程序的5个简单步骤
  • 3分钟搞定Windows和Office激活:KMS_VL_ALL_AIO智能脚本全解析
  • 3个月完成全链路升级:300人汽配制造企业SAP升级落地真实案例
  • 告别手忙脚乱:如何用League-Toolkit让英雄联盟游戏体验更丝滑
  • Docker Compose 深度剖析:一文打尽所有配置信息
  • 基于Spring Boot的智能停车导航与管理系统设计与实现
  • MPV播放器终极配置指南:从零构建专业级媒体播放体验
  • 阿里云Linux部署PHP项目:LNMP搭建+域名HTTPS+性能优化全流程
  • 前端周刊2026W22 | React 13周年、TanStack Router、Deno 2.8、Node.js 26、npm 分阶段发布
  • 【人工智能】Gemini回复:“Cherry studio跟Monica 选一个,你选谁?理由是?”
  • 机械泵维修方法?机械泵维修费用多少!
  • Windows系统文件dhcpcsvc6.dll文件丢失找不到问题解决