枚举与原生数据库类型
本教程共 54 篇 · 第 10 篇 · 更新于 2026-08-11 · 约 5 分钟阅读
本节目标:定义并使用 enum,掌握
@db.*原生类型与 SQLite 的限制。
枚举(enum)把字段的值限定在固定集合里。用户的角色只有 USER 和 ADMIN 几种,文章状态只有 DRAFT、PUBLISHED、ARCHIVED——用枚举建模,数据库和 TypeScript 两侧都会拦下非法值。
定义 enum 块
enum 块和 model 块平级,写在 Schema 顶层:
enum Role {
USER
ADMIN
}
model User {
id Int @id @default(autoincrement())
role Role @default(USER)
}
命名有约定:枚举名用 PascalCase 单数(Role),值用 UPPER_SNAKE_CASE(USER)。字段类型直接写枚举名,@default(USER) 指定默认值。枚举字段同样支持可选(Role?)和列表(Role[],PostgreSQL 支持)。
枚举值也能映射到数据库里的不同名字,用值级 @map:
enum Role {
USER
ADMIN @map("administrator")
}
代码里还是写 Role.ADMIN,数据库里存的是 administrator。枚举名整体映射用 @@map,用法与模型的 @@map 相同。
Note在 PostgreSQL 中,枚举会生成真实的
CREATE TYPE;SQLite 没有原生枚举,Prisma 在 ORM 层校验、以 TEXT 存储;MySQL 则是内联的 ENUM 列。MongoDB 与枚举同为 ORM 层实现(v7 不支持 MongoDB,留 v6.19)。
在代码里使用枚举
生成客户端后,枚举从生成目录导入(v7 路径,不再是 @prisma/client):
import { PrismaClient } from "./generated/prisma/client";
import { Role } from "./generated/prisma/enums";
const prisma = new PrismaClient({ adapter });
await prisma.user.create({
data: { email: "ada@example.com", role: Role.ADMIN },
});
过滤时枚举能参与全部比较操作符:
const admins = await prisma.user.findMany({
where: { role: { in: [Role.ADMIN, Role.EDITOR] } },
});
in、notIn、not、equals 都可用。更新枚举字段同样简单,直接赋新值:
await prisma.user.update({
where: { id: 1 },
data: { role: Role.EDITOR },
});
TS 里枚举是联合类型,switch 分支能获得穷尽检查,写错值编译期就报错。
Tip外部输入(比如 HTTP 请求参数)是字符串,先校验再转枚举,别直接
as Role强转。可以用z.nativeEnum(Role)(Zod)这类方案。
枚举迁移的安全边界
枚举改动会触发迁移,三种操作风险不同:
- 加值:安全。PostgreSQL 生成
ALTER TYPE ... ADD VALUE,追加到类型末尾。 - 重命名值:危险。底层是删旧值建新值,已有数据引用旧值会失败,需要先写数据迁移。
- 删值:危险。若还有行引用该值,删除直接报错。
改默认值也一样要迁移,DEFAULT 子句写在数据库层。所以枚举值一经发布,尽量只增不改。涉及枚举的迁移在 PostgreSQL 上生成 ALTER TYPE 语句,改默认值生成 ALTER TABLE ... ALTER COLUMN ... SET DEFAULT。
@db.*:指定原生类型
Prisma 的每种标量类型都有默认映射,比如 String 在 PostgreSQL 里是 text。想精确控制列类型,用 @db.* 属性。它只在两种情况下需要:默认类型不满足需求,或内省出的库本就用了特殊类型。
常用速查(PostgreSQL 主线):
| Prisma 类型 | 原生类型写法 | 场景 |
|---|---|---|
String | @db.VarChar(255) | 限制长度,配合索引 |
String | @db.Text | 长文本(默认即 text,可省略) |
String | @db.Uuid | 原生 UUID 列,配合 uuid() 默认值 |
String | @db.Citext | 大小写不敏感文本,需先启用扩展 |
Int | @db.SmallInt | 小整数,省空间 |
DateTime | @db.Timestamptz(6) | 带时区时间,精度 6 位 |
DateTime | @db.Date | 只存日期 |
Json | @db.JsonB | 二进制 JSON,支持更多查询操作 |
Decimal | @db.Decimal(12,2) | 金额,两位小数 |
model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(150)
createdAt DateTime @default(now()) @db.Timestamptz(6)
metadata Json @db.JsonB
}
迁移时,@db.VarChar(150) 会生成 VARCHAR(150) 列,@db.Timestamptz(6) 生成 TIMESTAMPTZ(6)——原生类型直接反映到建表 SQL。内省(prisma db pull)已有数据库时,如果列类型和 Prisma 默认映射不同,也会自动补上 @db.* 属性。
Tip时间字段选
Timestamptz(带时区)还是Timestamp(不带)是个经典取舍。存「这一刻」用带时区,能避免时区换算的坑;DateTime在 JS 侧始终是Date,差异只影响数据库层。
SQLite 的限制与 PG 扩展类型
SQLite 是轻量跟练线,限制要提前知道:标量类型只有 INTEGER、REAL、TEXT、BLOB,枚举靠 ORM 层实现,@db.* 基本无处可用,Json 也以 TEXT 存储。SQLite 适合学语法,生产环境换 PostgreSQL 即可平滑迁移。
PostgreSQL 的扩展类型则要先装扩展再用。比如 citext,通过自定义迁移执行 CREATE EXTENSION IF NOT EXISTS citext; 启用,之后才能写 @db.Citext。gen_random_uuid() 同理,PG 13 以下需先启用 pgcrypto。
参考来源
- Prisma 官方文档:Models(Defining enums、Native types mapping)
- Prisma 官方文档:Prisma schema reference(enum、@db 原生类型映射表)
- Prisma 官方文档:Native database types、PostgreSQL extensions