当前位置: 首页 > 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.gsyq.cn/news/1496370.html

相关文章:

  • linux下安装gitlab
  • 番禺洛浦奢侈品回收第一名|金小福名表名包名酒钻石翡翠黄金全品类专业回收 - 花生花生1
  • 2026年AI问答流量服务公司选购指南:技术架构、行业应用与决策框架 - 优质品牌商家
  • 2026 主流 GEO 源码厂商实测:云罗 GEO、摘星智能、棋引科技技术与落地能力对比
  • BiliBili-UWP桌面版终极秘籍:告别卡顿,打造你的专属B站体验
  • idea+git插件+云备份实现项目新分支新建维护
  • 前端周刊2026W22 | React 13周年、TanStack Router、Deno 2.8、Node.js 26、npm 分阶段发布
  • 防割面料采购怎么避坑,选UHMWPE梭织面料供应商为什么更稳
  • Android 权限请求构建器使用指南
  • 粗心和专注力有关系吗?
  • 七界梦谭长戟刚鬣怎么打 七界梦谭长戟刚鬣详细打法攻略
  • QQ本地缓存机制初步探寻
  • 2026年免费AI编程工具推荐榜单
  • Unity基础(十四)场景异步加载
  • OpenSpec实战
  • android开发 原生设置中的Device name 与Device model
  • 学习比特 享幸福人生
  • 2026高考大数据:1290万考生背后的赛道拥挤度与捡漏指南
  • GEO基础优化包含哪些基础项目
  • Redis中的通用命令
  • 论文去重难?5个实用工具帮你
  • Boss-Key:终极窗口隐私保护神器,一键隐藏桌面窗口的完整指南
  • 2026河马引力67W避坑:分配不均协议阉割散热差别买
  • Java 文件复制(字符 / 字节缓冲流)
  • 人形机器人进真实场景,开发者需要关注哪些技术栈?
  • 创建订单报错‘无定价过程被确定’
  • 水性机调色浆WM系列技术优势:纳米分散赋能高效调色
  • 计算机毕业设计之基于大数据的用户购物系统
  • cat命令将调试信息指定到对应路径下
  • G-Helper终极降压指南:AMD CPU温度直降15℃的完整实战教程