1. 课程导入与工程环境搭建
在 Node.js 与 TypeScript 生态中,访问关系型数据库长期存在两条主流技术路线:一条是以 Prisma 为代表的“Schema 优先 + 代码生成”路线,另一条是以 TypeORM 为代表的“装饰器实体 + 运行时映射”路线。两者都能完成增删改查、事务、关联查询与迁移,但它们的工程哲学、类型安全边界和团队协作方式差异明显。本章先建立整体定位认知,再完成一套可运行的环境,为后续章节的对比实验打好基础。
1.1 定位差异:Schema 优先与实体优先
Prisma 的核心是 schema.prisma 文件。开发者用 Prisma Schema Language 声明数据源、模型、字段类型与关系,随后执行 prisma generate,由 CLI 生成类型完备的 Prisma Client。数据库表结构、TypeScript 类型、查询 API 三者由同一份 Schema 派生,因此“模型改了但类型没跟上”这类问题在编译期就能暴露。它的迁移由 prisma migrate 管理,迁移文件是纯 SQL,可审阅、可版本化。
TypeORM 的核心是实体类。开发者用 @Entity()、@Column()、@ManyToOne() 等装饰器描述表结构,通过 DataSource 建立连接,用 Repository 或 QueryBuilder 发起查询。实体类既是数据库映射,也是业务代码可直接引用的类型,灵活度高,支持 Active Record 与 Data Mapper 两种风格。代价是装饰器元数据依赖 reflect-metadata 与 emitDecoratorMetadata,类型安全更多依赖开发者约束,复杂关联查询容易出现类型与运行时不一致。
1.2 适用场景判断
如果团队重视数据库结构的单一事实来源、希望查询结果类型与 Schema 严格一致、且愿意接受“改模型后必须重新生成客户端”的约束,Prisma 通常更省心。它特别适合中后台 API、Serverless 函数、需要频繁迭代表结构的早期项目。如果项目需要大量动态 SQL、复杂多表 JOIN、存储过程调用,或已有大量基于装饰器的实体代码,TypeORM 的 QueryBuilder 与原生 SQL 逃生舱会更顺手。它常见于传统 MVC 分层项目、需要精细控制 SQL 的场景。
需要强调的是,两者并非互斥。同一项目可以同时使用 Prisma 处理常规 CRUD、TypeORM 处理遗留模块,但会带来两套连接池、两套迁移体系的维护成本,除非有明确收益,否则不建议在同一个服务里混用。
1.3 Node.js 与 TypeScript 环境配置
建议使用 Node.js 20 LTS 或 22 LTS,TypeScript 5.x。先初始化项目并安装依赖:
mkdir prisma-typeorm-lab && cd prisma-typeorm-lab
npm init -y
npm i -D typescript ts-node @types/node
npx tsc --init
TypeORM 需要装饰器支持,tsconfig.json 中必须开启以下选项:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "dist"
}
}
Prisma 不依赖装饰器,但同样建议开启 strict,以便生成客户端的类型检查发挥最大作用。随后安装两套运行时:
npm i @prisma/client typeorm reflect-metadata pg
npm i -D prisma
注意 reflect-metadata 必须在应用入口最顶部导入一次,否则 TypeORM 的列类型推断会失败:
import 'reflect-metadata';
1.4 数据库选型与连接准备
本课程实验统一使用 PostgreSQL 16,原因是它同时被 Prisma 与 TypeORM 良好支持,且具备 JSONB、数组、枚举等便于演示高级特性的类型。若本机没有 PostgreSQL,可用 Docker 快速启动:
docker run -d --name pg-lab \
-e POSTGRES_USER=lab \
-e POSTGRES_PASSWORD=lab123 \
-e POSTGRES_DB=shop \
-p 5432:5432 postgres:16
连接串为 postgresql://lab:lab123@localhost:5432/shop。Prisma 在 .env 中通过 DATABASE_URL 读取;TypeORM 则在 DataSource 配置对象中显式传入。两者都建议把连接信息放在环境变量里,不要硬编码进源码。
mysql2 并调整连接串。1.5 初始化两套工程骨架
Prisma 侧执行:
npx prisma init --datasource-provider postgresql
这会生成 prisma/schema.prisma 与 .env。在 Schema 中先定义一个最小模型用于连通性验证:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
createdAt DateTime @default(now())
}
然后执行 npx prisma migrate dev --name init,它会创建数据库表、生成迁移文件并重新生成客户端。若命令成功且 node_modules/.prisma/client 出现,说明 Prisma 链路已通。
TypeORM 侧新建 src/data-source.ts:
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { User } from './entity/User';
export const AppDataSource = new DataSource({
type: 'postgres',
url: process.env.DATABASE_URL,
entities: [User],
synchronize: false,
logging: ['query', 'error'],
});
实体类用装饰器描述同样的表结构,随后调用 AppDataSource.initialize() 并执行一次 repository.find(),若能返回空数组且无报错,说明 TypeORM 链路也已打通。
1.6 环境自检清单
| 检查项 | Prisma 侧 | TypeORM 侧 |
|---|---|---|
| Node 版本 | ≥ 20 LTS | ≥ 20 LTS |
| 装饰器配置 | 不需要 | experimentalDecorators + emitDecoratorMetadata |
| 元数据导入 | 不需要 | 入口顶部 import 'reflect-metadata' |
| 连接来源 | .env 中 DATABASE_URL | DataSource 配置或环境变量 |
| 连通验证 | prisma migrate dev 成功 | initialize() 后 find() 无异常 |
| 迁移策略 | prisma migrate | migration:generate / run |