告别 ORM
2026年10月26日Elecmonkey
ORM 的辉煌与妥协
在过去的几年里(尤其指 AI Agent 普及之前),构建与数据库交互的后端,选定一个 ORM 加入技术栈中,我只会犹豫选什么,绝对不会怀疑需要一个 ORM 这件事本身。
究其原因,我觉得这首先是因为我是一个前端程序员,我不熟悉数据库、不了解 SQL,勉勉强强拼了命地写个 CRUD 还凑合。写 ORM 操作数据库,一来 API 查清楚和用别的第三方库没什么不同,感觉心理负担小很多;二来我实在不知道我自己手写出来的复杂 SQL 会不会有什么漏洞,竞争,乱七八糟的问题。ORM 至少对于 SQL 注入之类的常见问题它是要考虑的吧(当然源于业务逻辑本身的竞态 / 一致性风险肯定还是要自己关心的)。
总结下来,我觉得我主要在从 ORM 享受三个方面的好处:
- 让我不用手写我不擅长的 SQL
- 提供了类型化的接口,让数据库操作融入后端程序语言之中
- 以一种更符合 Web 应用开发模型、很 “数据” 的视角来看待数据库操作
ORM 无疑降低了对我 SQL 能力的要求,降低对 SQL 能力的要求也可以说就是降低了心智负担。类型化的接口对于良好的后端开发体验来说也是很重要的。类型化的数据获取 API 自然就能引出第三点,我不知道如何描述这种感受 —— 做个类比,写后端代码使用 ORM 操作数据库,感觉大抵类似我写前端代码使用 TanStack Query 操作服务端状态。
Agent 写 SQL
Agent 写 SQL 强于调用 ORM API
AI Agent 的高度普及改变了很多事情。在 2025 年的夏天我开始一个项目的时候,大多数同学 “听说” 的前端技术都是 Vue,是否要选型我自己更喜欢(并且我觉得生态更丰富,更不容易遇到问题)的 React 当时让我纠结了很久。
Vue 在 pre-LLM 时代是简单、易学的象征,对于只学过 Web 三件套的人直观明白。作为对全套构建链路有所了解的人,我自然会觉得 React 是更纯粹的 “JavaScript”,我可以很清楚地知道我写下的代码就是即将在浏览器里运行的代码(或者很容易想象在浏览器里运行的代码是什么样子的),但是对于没有这一层知识、只是把 React/Vue 框架当作 “应该这么写” 的知识点来记忆的人来说,Vue 更直观是显而易见的。大约和尤雨溪有关 —— Vue 在中文背景的开发者中更是普及。
仅仅一年半不到,我感觉已经几乎见不到关于 Vue or React 的讨论了。Agent 毫无悬念地选择 Next.js 或者 Vite + React,大量 “跨界” 而来做前端的开发者全盘接受。原因就是 React 普及,React 好写,React 对 AI 来说训练数据多。这是 AI 对于开源社区的一个显而易见的影响,强者愈强,流行的库更加流行。
反过来看 SQL vs. ORM 的讨论。SQL 历史悠久、高度标准化,MySQL / PostgreSQL 就算有方言成分也因为足够流行而让模型意外地擅长编写 SQL。ORM 几乎每个常见的后端语言都有那么两三个选择,有些还热衷于(玩笑) Breaking Change Prisma ORM 6 -> 7 升级实录,闲聊 Prisma 团队升级理念。诚然这些问题如今已经通过 Agent 会自主检索文档、Agent 去 node_modules 里翻箱倒柜、随包分发文档和 SKILLs 等手段在表面上得到了解决,但我的想法是对 AI 来说总归是不如训练数据里的 SQL 写着顺手。
Claude Opus 5.5:写 SQL 不烧心!
Agent 的普及让前文提到的第一个好处不成立了。
Typed SQL Runner 类工具的崛起
Prisma ORM 是我在 TypeScript 生态中从前用的比较多的 ORM 工具,Drizzle 也是比较有名的工具。它们的类型信息多来源于我按照它们所希望的方式来声明我要查询的内容或者被查询内容的结构,进而让它们可以从中推导出类型信息。
最近 TypeScript 7.1 Beta 版本发布,Content Mapper 终于可以试用了,一些热衷于造轮子的老哥很快就搞出了用 SQL 推导类型的 Content Mapper。
其实,这两者都是在通过尽可能地模拟数据库本身的行为逻辑来给出一个类型的结果,并不保证类型一定安全。更何况在 TypeScript 的语境下,类型信息本来就不是运行时的一部分。
Bun 的作者 Jarred Sumner 好像在研究让 TypeScript 类型成为运行时的一部分的一些方案。通过 AOT 来让 TypeScript 参与代码运行效率的提高。
名场面:
Jiahan Chen: That's BunScript, not TypeScript!
补充的补充:Jarred Sumner 后面有一条推文表示自己已经放弃了让类型断言参与运行时行为的设计。目前看来,主要的设计方向是在类型不符时报告运行时错误。
Rust 生态中的 SQLx 提供了一种不同于传统 ORM 的类型安全方案。直接在代码里写 SQL —— 这个时候 SQL 当然还是个字符串 —— SQLx 会在编译阶段(宏生成时)连接实际的数据库,利用数据库提供的查询分析能力,获取 SQL 参数及返回列的类型信息,并据此生成具有静态类型约束的 Rust 代码。SQLx 不去想方设法地模拟数据库的逻辑,而是将这部分工作交给真正的数据库服务器,无疑可以获取更准确的类型信息。
当然不是说后端开发每次编译都必须连一个真的数据库 —— SQLx 支持将查询分析结果预先缓存,然后把缓存结果提交到 Git 仓库中。通常情况下数据库 Schema 的变动应该是不频繁的。
显然,SQLx 的这种思路是使得 truth source 更统一了,对于获取类型化的接口这一目标来说,是更好的实现方案。我感觉各种 ORM 也可以考虑对自己允许直接写 SQL 的逃逸 API 采取这种方式获取类型,而不是允许随意断言了事。
数据库不是 Excel 表格
Bilibili 全网最尊重 MySQL 的博主 UP 主原子能关于数据库的视频还是给我留下了比较深的印象。他的一个核心论点就是很多后端的逻辑其实应该下沉到数据库层,甚至 PostgreSQL 可以承接大多数的后端逻辑。
在我的个人项目中,我可以很负责任地说我极大地浪费了关系型数据库所提供的能力。。我把数据库当做一个电子表格在用,我对数据库和 SQL 的有限了解,让我对它们的运用止步于此 —— 尽管我知道数据库有着强大得多的能力。
我觉得这就是我前文提到的 —— 以一种更 “数据” 的视角看待数据库操作所带来的弊端。在我自己的项目迁移中已经有所体现,在我把很多个人项目的后端用 Rust + Axum + SQLx 重写的过程中,已经多次发生了如下情况:原本的代码中使用 ORM 将很多数据读入内存中,随后由后端逻辑代码对数据进行计算处理,最后得到接口需要返回给前端的信息。而使用 SQL 直接表达相同的逻辑,可以直接从数据库拿到最终结果,对后端来说肯定是一个性能的提升和资源占用的下降,甚至对数据库来说可能也是优化(因为原本需要返回很多数据)。
我不排除里面很多情况是我对 ORM 的高级 API 不了解导致的,但是我想 ORM 抽象后的表达力不如 SQL 应该是毋庸置疑的。直接使用 SQL 无疑让我们可以更充分地利用数据库自身的能力,从实际情况出发考量逻辑的计算位置在后端 or 数据库。
解决与没有解决的问题
经过前面的讨论,也就不难理解我对 ORM 的前景持悲观态度的原因。ORM 的主要优势在 AI Coding Era 都不复存在或者在直接写 SQL 时也有了类似甚至更好的能力 —— 而 ORM 的主要劣势恰恰可以通过直接编写 SQL 来避免。
但也不是所有问题都被解决了。我曾经在代码里最担心的两类东西 —— 复制来的完全看不懂的正则表达式和复制来的完全看不懂的长 SQL。AI 编码和审查的能力很强,但并不是说一定能写出安全和优秀的 SQL —— 安全方面仍然需要有从前(pre-LLM 时代)知识水平的人来审查保障。我觉得这个问题其实不是 AI 引入的,完全不懂数据库原理的 CRUD 程序员在以前也有可能创造灾难,现在的 AI Agent 可能也需要人类的知识来为人类提供信心。更何况,很多不安全的操作往往是业务逻辑固有的 bug 或者和业务逻辑紧密相关,这不是 Agent 能解决的问题。
软件工程的重新权衡
当 AI 显著降低了直接使用底层能力的成本时,那些主要为简化开发而引入、却未提供足够独立价值的抽象,就值得被重新审视。
最近也有很多人在 X 上鼓吹 React 和前端框架都过时了,AI 能如何轻易地使用纯 HTML + JavaScript + CSS 构建 Web App。且不论这些人是否愿意在 tokens 上多花几倍的钱 —— 相比按现代前端实践搭建的有序 monorepo,我很难想象 AI 在驾驭几十万行混沌的 JavaScript 时,不会犯多得多的错误。另外,引入 React 等框架带来的成本目前在客户端其实几乎是没有感知的,真遇到了对渲染性能要求极其刁钻的场景,Svelte、Solid.js 以及即将推出的 Vue Vapor Mode 可能都是 AI 同样很擅长使用、在工程上也同样简单的替代品。
为什么 ORM 就不一样呢?我觉得,React 之于前端其实类似于 SQL 这一层之于数据库,它们把底层直接的 “执行” 抽象成了一种声明式的关系,不管是人类还是大语言模型,理解这一层关系都比理解复杂的底层执行过程轻松得多。ORM 在 SQL 之上提供的新抽象的创造性似乎有限,它更像一个译者,当我只能说中文时,译者是我与英语人士沟通的桥梁;但是当我能直接说英语时,请一位译者只会限制我对英语中某些微妙表达的使用。
Jiahan Chen: That's BunScript, not TypeScript!