Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Prisma 7 是一个现代化的数据库工具包,为 Node.js 和 TypeScript 应用程序提供了类型安全的数据库访问。它是 ORM(对象关系映射)工具的新一代解决方案,专注于开发者体验和类型安全。

基本使用

安装 Prisma

注意:本教程使用 pnpm 作为包管理器。如果您使用的是 npmyarn,请将命令中的 pnpm 替换为相应的命令。

1
2
3
4
5
# 安装 Prisma CLI 和 Client
pnpm install prisma @prisma/client

# 初始化 Prisma
pnpm prisma init

配置数据库连接

Prisma 7 提供两种配置方式:

传统 schema.prisma 配置(单文件模式)

适用于简单项目:

1
2
3
4
5
6
7
8
9
// prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
}

datasource db {
provider = "postgresql"
}

prisma.config.ts(Prisma 7 新特性,多文件模式)

适用于复杂项目,支持多 schema 文件和更灵活的配置:

1
2
3
4
5
6
7
8
9
10
// prisma.config.ts
import "dotenv/config";
import { defineConfig } from "prisma/config";

export default defineConfig({
schema: "prisma/prisma.schema",
datasource: {
url: process.env.DATABASE_URL!,
}
});

环境变量配置

1
DATABASE_URL="postgresql://username:password@localhost:5432/mydb"

定义数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

生成 Prisma Client

1
pnpm prisma generate

基本 CRUD 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
// 创建用户
const user = await prisma.user.create({
data: { email: 'alice@prisma.io', name: 'Alice' }
})

// 创建文章
const post = await prisma.post.create({
data: { title: 'Hello World', content: 'First post', authorId: user.id }
})

// 查询用户及其文章
const usersWithPosts = await prisma.user.findMany({ include: { posts: true } })

// 更新文章
const updatedPost = await prisma.post.update({
where: { id: post.id },
data: { published: true }
})

// 删除文章
await prisma.post.delete({ where: { id: post.id } })
}

main().catch(console.error).finally(async () => await prisma.$disconnect())

数据库迁移 (Migrate)

创建迁移

1
2
3
4
5
6
7
8
# 创建新的迁移 --create-only只创建迁移文件,不应用到数据库
pnpm prisma migrate dev --name add-user-model

# 查看迁移状态
pnpm prisma migrate status

# 重置数据库(开发环境)
pnpm prisma migrate reset

生产环境迁移

1
2
3
4
5
# 部署迁移到生产环境
pnpm prisma migrate deploy

# 解决迁移冲突
pnpm prisma migrate resolve

迁移最佳实践

  1. 在版本控制中管理迁移文件:所有的迁移文件都应该提交到 Git 仓库。

  2. 使用描述性的迁移名称:迁移名称应该清楚地描述所做的更改。

  3. 测试迁移:在生产环境应用迁移之前,先在测试环境中测试。

  4. 备份数据库:在生产环境运行迁移之前,始终备份数据库。

数据种子 (Seed)

创建种子文件

创建 prisma/seed.ts 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
// 创建测试用户
const alice = await prisma.user.upsert({
where: { email: 'alice@prisma.io' },
update: {},
create: {
email: 'alice@prisma.io',
name: 'Alice',
posts: {
create: {
title: 'Hello World',
content: 'This is Alice\'s first post',
published: true,
},
},
},
})

const bob = await prisma.user.upsert({
where: { email: 'bob@prisma.io' },
update: {},
create: {
email: 'bob@prisma.io',
name: 'Bob',
posts: {
create: [
{
title: 'Prisma 7 新特性',
content: 'Prisma 7 带来了许多激动人心的新功能',
published: true,
},
{
title: 'TypeScript 最佳实践',
content: '分享一些 TypeScript 开发经验',
published: false,
},
],
},
},
})

console.log({ alice, bob })
}

main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})

配置 package.json

package.json 中添加种子脚本:

1
2
3
4
5
{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}

运行种子脚本

1
2
3
4
5
# 运行种子脚本
pnpm prisma db seed

# 在重置数据库后自动运行种子
pnpm prisma migrate reset

高级种子技巧

使用 Faker.js 生成测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { faker } from '@faker-js/faker'

async function createRandomUsers(count: number) {
const users = []

for (let i = 0; i < count; i++) {
users.push({
email: faker.internet.email(),
name: faker.person.fullName(),
posts: {
create: Array.from({ length: faker.number.int({ min: 0, max: 5 }) }, () => ({
title: faker.lorem.sentence(),
content: faker.lorem.paragraphs(),
published: faker.datatype.boolean(),
})),
},
})
}

return users
}

条件种子

1
2
3
4
5
6
7
8
9
10
async function conditionalSeed() {
const userCount = await prisma.user.count()

if (userCount === 0) {
// 只在数据库为空时运行种子
await main()
} else {
console.log('Database already has data, skipping seed')
}
}

Prisma Schema 设计最佳实践

1. 明确业务模型

  • 避免过度规范化或反规范化
  • 只在真正需要时使用关系字段
  • 适度冗余以减少 JOIN 查询(读多写少场景)

2. 合理使用关系

1
2
3
4
5
6
7
8
9
10
model User {
id Int @id @default(autoincrement())
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
  • 避免循环依赖
  • 根据查询需求选择单向或双向关系

3. 字段规范

  • 使用语义清晰的字段名(如 createdAt
  • 优先使用 Prisma 内置标量类型
  • 枚举类型替代字符串硬编码
1
2
enum UserRole { ADMIN USER MODERATOR }
model User { role UserRole @default(USER) }

4. 时间戳管理

1
2
3
4
model Post {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

5. 主键设计

  • 优先使用自增整数主键:@id @default(autoincrement())
  • 分布式系统可考虑 CUID 或 UUID:id String @id @default(cuid())

6. 删除策略

  • 软删除:使用 deletedAt DateTime? 保留历史记录
  • 硬删除:在应用层逻辑控制删除行为

7. 性能优化

  • 对高频查询字段添加索引
  • 避免在大表上频繁使用 Json 字段查询

8. 版本控制

  • 将 schema.prisma 和 migrations/ 目录纳入 Git
  • 使用 prisma migrate dev 本地开发,prisma migrate deploy 生产部署

9. 多 Schema 文件模式

为什么需要多 Schema 文件?

随着项目规模增长,单个 schema.prisma 文件会变得庞大且难以维护。多 schema 文件模式允许我们将模型定义分散到多个文件中,提高代码的可维护性。

设置多 Schema 文件

在 Prisma 7 中,使用多 schema 文件模式非常简单,只需要在 prisma.config.ts 中指定 schema 目录即可,Prisma 会自动加载和处理该目录下的所有 .prisma 文件。

1. 配置 prisma.config.ts

首先,在 prisma.config.ts 中启用多 schema 支持并指定目录:

1
2
3
4
5
6
7
8
9
10
11
12
// prisma.config.ts
import { defineConfig } from 'prisma'

export default defineConfig({
// 指定包含多个 schema 文件的目录
schema: "prisma/models",

datasource: {
provider: "postgresql",
url: process.env.DATABASE_URL,
}
})

或者,您也可以在命令行中指定 schema 目录:

1
2
3
4
5
# 指定 schema 目录
pnpm prisma generate --schema prisma/models

# 或者在 package.json 中配置
pnpm prisma generate --schema prisma/models/*.prisma
2. 创建模型文件

现在您可以在 prisma/models/ 目录下创建多个 .prisma 文件,Prisma 会自动加载所有这些文件:

创建 prisma/models/base.prisma(基础配置):

1
2
3
4
5
6
7
8
9
// prisma/models/base.prisma
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

创建 prisma/models/user.prisma(用户模型):

1
2
3
4
5
6
7
8
9
10
11
12
13
// prisma/models/user.prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
comments Comment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([email])
@@map("users")
}

创建 prisma/models/post.prisma(文章模型):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// prisma/models/post.prisma
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
comments Comment[]
tags Tag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([authorId])
@@map("posts")
}

创建 prisma/models/comment.prisma(评论模型):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// prisma/models/comment.prisma
model Comment {
id Int @id @default(autoincrement())
content String
post Post @relation(fields: [postId], references: [id])
postId Int
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@index([postId])
@@index([authorId])
@@map("comments")
}

创建 prisma/models/tag.prisma(标签模型):

1
2
3
4
5
6
7
8
9
10
11
// prisma/models/tag.prisma
model Tag {
id Int @id @default(autoincrement())
name String @unique
description String?
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@map("tags")
}

评论