Prisma ORM 实战指南:用 Prisma Schema、Prisma Client 与 Prisma Migrate 告别手写 SQL
【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum
本指南基于
cu/curriculum(The Odin Project 开源全栈 Web 开发课程)中的nodeJS/orms/prisma_orm.md一课展开,系统讲解 Object Relational Mapper(ORM)的核心价值,并以 Prisma ORM 为主线,深入拆解 Prisma Schema 数据建模、Prisma Client 类型安全查询与 Prisma Migrate 数据库迁移三大核心组件。读完本文,你将理解为什么大型团队需要统一的数据库交互标准,掌握用 Prisma 定义模型关系、生成客户端、执行迁移的完整工作流,并能在 Express 项目中落地一套可维护、可测试的数据库访问层。
引言:为什么需要 ORM
在完成前面多个项目后,你大概率已经体会到了手写 SQL 的繁琐。ORM(Object Relational Mapper,对象关系映射器)正是为了解决这一问题而生的工具——它让你能够用代码操作数据库中的数据,并且在整个软件行业中得到了广泛应用。在 Node.js 生态中,ORM 的选择非常多,社区至今没有形成唯一的"事实标准",而本课程之所以选择 Prisma ORM,正是因为它在 Node.js 开发者群体中的流行度和社区支持力度。
Prisma ORM 并不是一个单一的库,而是由多个库组成的一套工具链,因此你可以通过 npm 按需安装应用所需要的部分。它具备本课程后续全部项目所需的能力,并且还有更多扩展功能。
原始 SQL 的三大痛点
在引入 Prisma 之前,先来看看纯手写 SQL 在真实项目中会遇到哪些问题。
痛点一:代码量成倍膨胀
需要一条SELECT查询?写一条 SQL。需要查询另一张表?再写一条。程序员的本能会促使你抽一个SELECT的工具函数,但紧接着需求变成"只查询特定列"时,你又要去改这个工具函数;再来筛选(filters)、排序(sorting)呢?思路立刻开始蔓延。插入(insert)查询和它的各种变体也是如此。
于是出现了几种组织方式:
- 按实体建模块:例如为书籍实体创建
Book类,把数据库操作封装成方法:
class Book { async getBooks(filters) {} async getBookById(id) {} async createBook(data) {} async updateBook(id, data) {} async deleteBook(id) {} async getBookAuthors(id) {} async getBookGenres(id) {} // and so on }- 继承式封装:创建一个
Database基类,让各实体类继承它。 - 组合式封装:使用纯函数组合来完成数据库操作。
这些思路本身没有错——对个人项目而言,多写代码意味着多练习,课程甚至鼓励你去动手尝试。但当你进入团队协作、面对大型软件时,"标准化的数据库交互方式"(无论是引入外部库还是自研方案)就变得至关重要。你会发现 ORM 真正帮你把精力解放出来,聚焦到业务关键代码上。
课程建议:重构你之前的项目如果你在之前的项目中还没有应用过上述任何一种范式,强烈建议你回去做一次重构。你可能会意外得到一个属于你自己的"迷你 ORM",这能让你更真切地体会到成熟 ORM 带来的便利。
痛点二:无法从代码库读懂数据库结构
当所有数据库交互都散落在原始 SQL 中时,代码库里没有任何地方能让你一眼看清:数据库有哪些表、表之间是什么关系、每列是什么数据类型。你可能不得不登录数据库去反推代码在做什么——要理解项目,你就得同时依赖代码库和数据库两套信息源。
大多数 ORM 通过把数据库定义"搬进代码库"来解决这个问题,这份定义就叫schema(模式)。有了 schema,你只需扫一眼某个表的模型定义,就能知道它有哪些列、类型是什么、与哪些表存在关联。
痛点三:生产数据的迁移(Migration)
随着项目需求演进,数据库几乎必然要变化——新增一列、用已有数据填充新表,这在专业术语中叫做迁移(migration)。没有 ORM 或类似库的辅助,你只能手工编写这些迁移脚本,既容易出错又极其枯燥。ORM 通过**变更日志(changelog)**标准化迁移流程,并提供处理冲突的机制。当然,课程项目里你不需要频繁迁移,但在职业开发中,这可能每隔一两天就要做一次。
Prisma ORM 概览
ORM 基本能解决上面提到的所有痛点,但并非没有代价:学习曲线是真实存在的,而且部分 ORM 并不能完整支持所有 SQL 特性。即便如此,使用 ORM 依然是极其值得的投入。
Prisma ORM 由多个库组成,核心三件套是:
| 组件 | 作用 | 对应概念 |
|---|---|---|
| Prisma Schema | 用 Prisma Schema Language 定义数据模型与关系 | 数据库表结构 |
| Prisma Client | 按 Schema 自动生成、类型安全的查询客户端 | 数据访问层 |
| Prisma Migrate | 将 Schema 变更同步到数据库并记录迁移历史 | 数据库迁移 |
下文逐一深入。
Prisma Schema:在代码中定义数据模型
Prisma schema 是一个用Prisma Schema Language(PSL)编写的文件,你在这里定义所有模型(model)。以聊天应用中的消息表为例:
model Message { id Int @id @default(autoincrement()) content String @db.VarChar(255) createdAt DateTime @default(now()) author User @relation(fields: [authorId], references: [id]) authorId Int } model User { // user's fields }这段代码包含不少新信息,逐一解读:
- 列定义:
id Int @id @default(autoincrement())声明主键并自增;content String @db.VarChar(255)声明类型为长度 255 的字符串;createdAt DateTime @default(now())声明默认值为当前时间。 - 关系定义:
author User @relation(fields: [authorId], references: [id])表明Message通过authorId外键关联到User表的id列。注意,Prisma 的关系不是孤立的——它要求关系两侧的模型(User侧也需要对应的关系字段)都正确声明才能通过校验。
这份 schema 文件存在于你的代码库中,并受版本控制跟踪。它的价值不言而喻:新成员查看代码即可理解数据模型,数据库结构随代码一起演进、可审查、可回滚。
Prisma Client:按 Schema 生成的类型安全客户端
Prisma Client 是一个独立的库,用于与数据库交互。它很特别的一点是:它是为你的 schema 量身定制的。什么意思?看代码:
// instantiate the client import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); // when creating a new message await prisma.message.create({ data: { content: 'Hello, world!', authorId: 1 } }) // when fetching all messages const messages = await prisma.message.findMany();注意prisma.message这个对象——Prisma Client 怎么知道存在一个message模型?秘诀在于:每当你创建或更新 schema 文件后,只需在 CLI 中运行:
npx prisma generatePrisma ORM 就会重新生成客户端。生成后的客户端具备完整查询能力:**联表查询(joins)、过滤(filters)、排序(sorting)、分页(pagination)**等一应俱全。
如果遇到 Prisma Client 难以表达的复杂查询,或者你就是更习惯写原生 SQL,Prisma Client 同样支持raw queries(原生查询),两种方式可以按需混用。
Prisma Migrate:数据库迁移工具
Prisma Migrate 是帮助你执行数据库迁移的工具。课程项目中你不会频繁使用它,但了解它是必要的:当你想以任何方式修改 schema 时,运行一次 Prisma 迁移即可把 schema 变更应用到数据库。这些变更会被记录在代码库中的migrations文件夹里,形成可追溯的迁移历史——这正是前文提到的"用 changelog 标准化迁移、处理冲突"机制的落地形态。
典型的迁移工作流是:
- 修改 Prisma schema 文件(例如新增字段、新建模型);
- 运行迁移命令(如
npx prisma migrate dev)生成迁移 SQL 并应用到数据库; - 必要时再次运行
npx prisma generate让客户端感知新的数据模型。
注意:Prisma ORM 的已知限制在课程的 Using PostgreSQL lesson 中我们学过 Identity 列——PostgreSQL 官方推荐使用 Identity 列,因为它符合 SQL 标准。但Prisma ORM 不支持 Identity 列,它会改用 PostgreSQL 专有的Serial 类型来实现自增。这对大多数项目没有影响,但值得记在心里——尤其是当你需要精确控制自增列行为时。关于 Serial 与 Identity 的差异,PostgreSQL 官方文档的数值类型章节有详细说明。
开发体验:VS Code 官方扩展
如果你使用 VS Code,可以安装 Prisma 官方扩展来提升 schema 文件的编写体验,它提供:
- 语法高亮(syntax highlighting);
- IntelliSense / 自动补全(auto-completion);
- schema 静态检查(schema linting);
- 模型间的便捷跳转(navigation between models)。
配合该扩展,编辑 Prisma schema 文件的体验会舒适很多。
快速上手:在 JavaScript 项目中配置 Prisma(针对官方 Quickstart 的适配)
课程作业要求跟随 Prisma 官方 Quickstart 完成一个 PostgreSQL 快速上手项目。需要注意的是:Prisma 近期已决定只继续支持 TypeScript,因此需要使用 JavaScript 的同学必须对官方 Quickstart 的步骤做如下调整:
Step 1:跳过以下命令(它们是为 TypeScript 项目初始化准备的):
npm init npm install typescript tsx @types/node --save-dev npx tsc --initStep 2:无需安装@types/pg。若出现 "install scripts not yet covered by allowScripts" 之类的警告,可安全忽略——Prisma 会在后续步骤中自行执行必要操作。
Step 3:我们不使用 TypeScript,因此完整跳过该步骤。
Step 4:我们要使用prisma-client-js生成器而非默认生成器,因此在 Prisma init 命令中追加--generator-provider prisma-client-js;同时追加--no-skills以默认不安装 Prisma Skills 目录中的任何 AI 功能:
npx prisma init --datasource-provider postgresql --output ../generated/prisma --generator-provider prisma-client-js --no-skills此外,需要把生成的prisma7.config.ts重命名为prisma7.config.js。
Steps 5 和 6:无需任何修改。
Step 7:官方使用lib/prisma.ts,这里改为创建lib/prisma.js,并且导入PrismaClient时必须带.js文件扩展名:
import { PrismaClient } from '../generated/prisma/client.js';Step 8:创建的脚本文件命名为script.js,导入prisma时同样加上.js扩展名,首行应为:
import { prisma } from './lib/prisma.js';运行脚本使用node script.js。
Step 9:无需任何修改。
完成 Quickstart 后,建议继续阅读 Prisma 官方文档中以下主题(对照课程节奏,建议边读边敲代码,暂时记不住也没关系,后续项目会大量练习):Prisma ORM 是什么、Prisma schema 总览、数据模型(models)、关系(relations)、Prisma Client 的 CRUD 操作、Raw SQL 用法、Prisma Migrate 入门及其心智模型、数据迁移(data migrations)。
在课程项目中的实际落地
prisma_orm.md这节课不是终点,而是为后续项目铺路。仓库中可以看到 Prisma 在课程体系中的具体落点:
实战项目:文件上传应用
nodeJS/orms/project_file_uploader.md 要求基于 Express + Prisma 构建一个精简版"个人网盘",核心步骤包括:
- 安装 Express、Prisma 及 Passport 等依赖,建立项目骨架;
- 基于 Passport.js 实现会话认证,并使用Prisma session store 库把会话持久化到数据库;
- 借助 multer 中间件实现文件上传(先保存到文件系统);
- 实现文件夹的 CRUD 与目录内文件上传,为此要定义路由和对应的数据库交互——这正是 Prisma Client 的用武之地;
- 提供文件详情页(名称、大小、上传时间)与下载按钮;
- 最后把文件上传改为对象存储方案(如 Cloudinary 或 Supabase storage),数据库中仅保存文件 URL;
- 自行设计文件校验(限制文件类型和/或限制文件大小)。
可见,Prisma 在项目中的角色是"数据访问层":schema 定义文件夹、文件等模型及关系,Client 处理 CRUD,Migrate 负责表结构演进。
数据库测试:测试环境如何与 Prisma 协作
nodeJS/testing_express/testing_database_operations.md 展示了 Prisma 与测试流程的结合方式。要点包括:
- 为测试创建独立数据库(建议以
test_前缀命名),可通过临时切换数据库 URL 后执行 prisma migrate,或使用 seed 脚本、在beforeEach中手动插入数据来完成初始化; - 通过环境变量区分数据库,例如在
.env中同时配置DATABASE_URL与TEST_DATABASE_URL,并在代码中按NODE_ENV切换:
const connectionString = process.env.NODE_ENV === 'test' ? process.env.TEST_DATABASE_URL : process.env.DATABASE_URL; const adapter = new PrismaPg({ connectionString }); const prisma = new PrismaClient({ adapter });- 测试之间隔离数据:在
beforeEach中使用事务清空相关表(如prisma.$transaction([prisma.user.deleteMany(), prisma.project.deleteMany()])),并让测试按文件串行执行(如 Jest 的--runInBand),避免并行测试互相污染。
前置知识:PostgreSQL 基础
使用 Prisma 之前,你需要先掌握 PostgreSQL 本身。仓库中的 nodeJS/express/using_postgresql.md 覆盖了创建数据库、建表、使用 node-postgres(pg)查询、参数化查询防 SQL 注入、脚本填充数据库等内容;对应平台的安装指南可参考 nodeJS/express/installation_guides/postgresql/linux.md 与 nodeJS/express/installation_guides/postgresql/macos.md。有了这些基础,再进入 Prisma 会顺畅得多——因为 Prisma 生成的迁移 SQL、@db.VarChar等类型映射,本质上都是围绕 PostgreSQL 的特性展开的。
知识自检
以下问题用于回顾本课关键知识点(不必死记硬背,回答不上来就回看对应小节):
- 使用原始 SQL 会面临哪些挑战?
- 什么是 Prisma schema?它为什么有用?
- 什么是 Prisma Client?
- Prisma Client 是如何知道 schema 中有哪些模型的?
- 什么是 Prisma Migrate?
- 如何在 Prisma schema 中定义模型间的关系?
- 如何使用 Prisma Client 获取表中的全部记录?
附加资源
如需继续深入,可参考课程配套的 Prisma Crash Course 视频(Traversy Media 出品),并结合本仓库中的实战项目文档 nodeJS/orms/project_file_uploader.md 与 nodeJS/testing_express/testing_database_operations.md 动手实践,将本文的理论转化为可运行的代码。
【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考