本节目标:理解数据库操作的核心概念(CRUD、ORM、事务),学会审查 AI 生成的数据库代码。
ORM(Object-Relational Mapping,对象关系映射) 是一个"翻译层",让你用编程语言(TypeScript/JavaScript)来操作数据库,而不需要手写 SQL。
你可以把 ORM 想象成一个翻译官:你说中文(TypeScript 代码),数据库只懂英文(SQL),ORM 负责在中间翻译。你写 db.insert(users).values({ phone: '138xxxx', name: '小红' }),ORM 翻译成 INSERT INTO users (phone, name) VALUES ('138xxxx', '小红') 发给数据库执行。
为什么需要这个翻译官?直接写 SQL 不行吗?
直接写 SQL 的问题。 SQL 是数据库的"母语",功能强大但有几个痛点:字符串拼接容易出错、没有类型检查(写错列名要到运行时才发现)、不同数据库的 SQL 语法有差异。对于 Vibe Coder 来说,最大的问题是——你让 AI 生成 SQL,很难一眼看出对不对。一条复杂的 SQL 可能有十几行,嵌套三四层,光是括号匹配就让人头晕。
ORM 的好处。 ORM 把数据库表映射成代码里的对象,把 SQL 操作映射成函数调用。列名有自动补全、类型有检查、写错了编辑器直接报红。而且 ORM 生成的代码更容易阅读和审查——你看到 db.select().from(users).where(eq(users.phone, '138xxxx')),即使不懂 SQL 也能猜到它在做什么。
本教程推荐 Drizzle ORM,原因是:
另一个流行的选择是 Prisma,它用自己的 Schema 语言定义表结构,语法更简洁但离 SQL 更远。两者都是好工具,选一个坚持用就行。重要的不是选哪个,而是理解 ORM 背后的概念——因为概念是通用的,工具可以换。
作为 Vibe Coder,你不需要自己写 CRUD 代码——AI 会帮你写。但你需要知道怎么审查 AI 生成的代码。
这就像你请了一个装修工人。你不需要自己刷墙、铺地板,但你得知道墙刷得平不平、地板铺得正不正。如果你完全不懂装修,工人偷工减料你也看不出来。
AI 生成的数据库代码也是一样。它大部分时候是对的,但偶尔会犯一些"看起来能跑但其实有隐患"的错误。以下是每种操作的审查要点:
Create(插入)审查清单:
Read(查询)审查清单:
user_id 查还是按 phone 查)Update(更新)审查清单:
updated_at 时间戳?Delete(删除)审查清单:
Update 和 Delete 操作如果忘了写 WHERE 条件,会影响表里的所有行。这是数据库操作中最常见也最致命的错误。
想象你想把小红的地址改成"3 号楼 201",结果 AI 生成的代码忘了加 WHERE 条件——整张 users 表里所有用户的地址都被改成了"3 号楼 201"。几千个用户的地址,一瞬间全变了。
审查 AI 代码时,看到 UPDATE 或 DELETE,第一件事就是检查有没有 WHERE。没有 WHERE 的 UPDATE/DELETE 几乎永远是 bug。
小红的平台上,用户下单时需要同时做三件事:
如果第 1 步成功了,第 2 步也成功了,但第 3 步扣库存时发现库存不足——怎么办?订单已经创建了,但菜品没库存了,这就是数据不一致。用户以为下单成功了,商家一看库存已经是负数了,两边都懵。
事务(Transaction) 就是解决这个问题的。事务把多个操作打包成一个"原子操作":要么全部成功,要么全部回滚(撤销),不会出现"做了一半"的中间状态。
"原子"这个词来自物理学——原子是不可再分的最小单位。事务也是一样,它是数据库操作的最小单位,不存在"执行了一半"的状态。
用一个更生活化的例子来理解:你去 ATM 转账,从 A 账户转 100 块到 B 账户。这个操作包含两步:A 扣 100、B 加 100。如果 A 扣了钱但 B 没加上(比如系统崩溃了),那 100 块就凭空消失了。事务保证这两步要么都完成,要么都不做——如果中途出错,A 扣的钱会自动退回来。
事务的四个特性(ACID):
| 特性 | 含义 | 小红的例子 |
|---|---|---|
| Atomicity 原子性 | 全成功或全失败 | 下单三步要么都完成,要么都撤销 |
| Consistency 一致性 | 操作前后数据都合法 | 库存不会变成负数 |
| Isolation 隔离性 | 并发操作互不干扰 | 两人同时抢最后一份,只有一人成功 |
| Durability 持久性 | 成功后数据永久保存 | 服务器重启,订单数据还在 |
你不需要自己实现事务逻辑——AI 会用 ORM 的事务 API 帮你包装好。你只需要审查:哪些操作应该放在同一个事务里。
判断标准很简单:如果几个操作必须同时成功或同时失败,就放在一个事务里。
常见需要事务的场景:
事务执行期间会持有数据库锁,阻塞其他操作。如果事务里包含了调用外部 API、发送邮件等耗时操作,锁会被长时间占用,导致其他用户的请求排队等待。
想象你在银行柜台办业务,柜员帮你转完账后,又帮你打电话确认、发短信通知、打印回执……后面排队的人全在等。正确的做法是:柜员只做转账这一件事(数据库操作),转完账你就离开柜台,打电话、发短信这些事在旁边自己办(事务之外)。
原则:事务里只放数据库操作,外部调用放在事务之外。比如"创建订单 + 发通知",应该先在事务里创建订单,事务提交后再发通知。
表结构不是一成不变的。随着功能迭代,你可能需要给 users 表加一个 avatar 字段,或者给 posts 表加一个 is_pinned 列。
直接改数据库?太危险了。万一改错了,线上数据就毁了。而且你怎么记得住自己改了什么?三个月后回头看,完全想不起来当初为什么加了这个列。
迁移(Migration) 是数据库的"版本管理",就像 Git 管理代码版本一样。每次修改表结构,都生成一个迁移文件,记录"做了什么改动"。这样可以:
revertDrizzle ORM 有内置的迁移工具。工作流程是这样的:你修改 Schema 代码(比如给 users 表加一个 avatar 列),然后运行迁移命令,工具会自动对比新旧 Schema 的差异,生成一个迁移 SQL 文件(比如 0001_add_avatar_to_users.sql),最后执行这个文件来更新数据库。
开发环境随便改,但生产环境的迁移要小心。有些操作是不可逆的:删掉一个列,里面的数据就永远没了;把 text 改成 integer,不符合格式的数据会丢失。
AI 生成迁移后,一定要先在开发环境测试,确认无误再应用到生产。特别是涉及删列、改类型的操作,要格外谨慎。如果不确定,先备份。