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
- 性能底线:SQL 可控、可 explain、DBA 可审核;
- 排查效率:线上慢了能快速定位到具体 SQL;
- 复杂查询能力:join/聚合/报表/分页/动态条件是主场;
- 结果映射强:复杂结果集映射到 DTO 更顺手;
- 效率不吃亏:MyBatis-Plus还能让简单CRUD也很快。
最近在知乎出了
- 「应付6000万会员的秒杀系统专栏」
- 「几亿用户,百万并发的C端商品系统实战」
- 「技术团队DDD领域驱动设计三年落地实战」
- 「应付亿级用户规模的支付系统代码实战」
- 「应付亿级用户的会员体系代码实战」
专栏,感兴趣的可以订阅一下。至于知识星球的,可以搜:
- 老码头的技术浮生录
它是一个能实际帮你解决难题的星球。有问题的,找知心的Sam哥,支持无限次语音一对一解决你遇到的难题。「另外后续我新写的所有对外的付费专栏,在星球内都是免费的,且可以拿到所有源代码。」
当前星球里免费看的专栏是:
- 「应付6000万会员的秒杀系统专栏」
- 「几亿用户,百万并发的C端商品系统实战」
- 「技术团队DDD领域驱动设计三年落地实战」
- 「应付亿级用户规模的支付系统代码实战」
- 「应付亿级用户的会员体系代码实战」
知识星球内后续将推出20+个付费专栏,覆盖电商全链路:
| 选购线 | 用户会员营销线 | 中后台 |
|---|---|---|
| 购物车服务 | 营销系统 | 订单系统 |
| 商品服务 | 用户系统 | 支付系统 |
| 菜单服务 | 结算服务 |
从前台选购到中后台结算,星球成员全部免费,后续新增也不额外收费。
我的知乎账号:
- SamDeepThinking
