接手一个后台项目,列表页只查 20 条数据,日志里刷了 40 多条 SQL,典型的 N+1。orm框架用起来省事,但把 SQL 藏起来之后,性能问题也跟着藏起来了。

它到底帮你做了什么

ORM 把数据库表映射成类,一行记录就是一个对象,增删改查不用手写 SQL。Java 的 MyBatis、Hibernate,Python 的 SQLAlchemy,Node 的 Sequelize,都是常见选择。字段类型转换、参数绑定、事务管理这些重复活,它替你干了。

省事的地方很实在

小项目、后台管理、字段经常改的早期阶段,改一个字段只动模型定义,不用满项目找 SQL。参数化查询默认就做了,拼接字符串导致的注入问题少很多。关联查询用链式写法,可读性比一大段 SQL 好。

N+1 和复杂查询是两道坎

循环里逐条查关联表就是 N+1,20 条主记录会变成 21 次查询。解决办法是预加载,比如 JPA 的 EntityGraph、Sequelize 的 include。报表类的多表聚合、窗口函数,ORM 写出来又长又难调,直接写 SQL 更痛快。

怎么选比较实际

业务简单、团队人少,用 ORM 提效明显。查询复杂、性能敏感的核心接口,保留手写 SQL 的出口,MyBatis 这种半自动映射框架就是折中方案。别为了「纯」把复杂 SQL 硬塞进 ORM 的表达式里,后面维护的人会骂人。

出问题先看它发了多少条 SQL

把 SQL 日志打开,看一次请求到底发了几条语句。Spring 项目里开 show-sql,Node 的 ORM 大多有 logging 选项。懒加载在事务外触发会报 LazyInitializationException,这种报错多半是关联对象拖到模板渲染时才去查库。

批量插入用 saveAll 或者 batch 配置,一条条 insert 慢得离谱。分页查询还要留意 count 语句,有些 ORM 会为每个关联额外生成查询,列表接口的耗时就是这么堆出来的。

一句话判断:CRUD 为主用 ORM,报表和批量操作放手写 SQL。orm框架是工具,不是信仰,混着用才正常。数据库迁移工具跟 ORM 配合起来,字段变更才有记录可查。