介绍
Prisma 是一个现代的数据库工具,旨在简化数据库访问和操作。它提供了一种类型安全的方式来与数据库进行交互,并且可以与多种数据库(如 PostgreSQL、MySQL、SQLite 等)一起使用。Prisma 的核心组件包括 Prisma CLient、Prisma Migrate 和 Prisma Studio。
使用
安装
pnpm install prisma -D使用
npx prisma <command>指令:
- init: 初始化 Prisma 项目,生成
prisma/schema.prisma文件。 - generate: 生成 Prisma 客户端代码。
- migrate: 创建迁移文件,用于数据库架构的变更。
- studio: 启动 Prisma Studio,一个图形化的数据库管理工具。
- introspect: 生成 Prisma schema 文件,用于描述数据库架构。
- validate: 验证 Prisma schema 文件的有效性。
- format: 格式化 Prisma schema 文件。
prisma db pull
prisma migrate dev --name init
prisma generate初始化
npx prisma init- 初始化
prisma项目,创建prisma目录(包含prisma.prisma文件)和.env文件(用于数据库连接配置)。
- 初始化
数据库 schema 管理
npx prisma migrate dev --name <migration-name>- 根据
schema.prisma的变更生成迁移文件,并应用到开发数据库。--name用于指定迁移名称(必填)。 - 需要添加影子数据库连接
SHADOW_DATABASE_URL,在.env和prisma.schema中配置 - 会在 prisma 目录中生成一个
migrations迁移文件目录
- 根据
npx prisma migrate deploy- 将迁移文件应用到生产环境数据库(不生成新迁移,仅执行已有的迁移)。
npx prisma migrate reset- 重置数据库(删除数据、重新应用所有迁移),常用于开发环境。
npx prisma db pull- 根据现有数据库结构自动生成
schema.prisma模型(反向工程)。
- 根据现有数据库结构自动生成
npx prisma db push- 直接将
shcema.prisma的变更推送到数据库(不生成迁移文件,适合快速原型开发)。
- 直接将
影子数据库
- 在 prisma 中,影子数据库主要用于开发阶段检测模式漂移或生成迁移时可能的数据丢失问题。运行
prisma migrate dev命令时,prisma 会自动创建和删除影子数据库。但在生产环境中,通常不需要影子数据库,生产关系命令如prisma migrate resolve和prisma migrate deploy不会使用它。
.env
SHADOW_DATABASE_URL="mysql://remove_watermarks:123456@localhost:3306/prisma_shadow_db"prisma.schema
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}客户端生成
npx prisma generate- 根据
schema.prisma生成 Prisma Client(类型安全的数据库访问客户端),每次修改 schema 后需执行。
- 根据
数据库交互
npx prisma studio- 启动 prisma 可视化界面(默认地址
http://localhost:5555),可直观查看和编辑数据库数据。
- 启动 prisma 可视化界面(默认地址
npx prisma db seed- 执行种子脚本(需在
package.json中配置prisma.seed指向脚本),用于初始化测试数据。
- 执行种子脚本(需在
其他实用指令
npx prisma migrate status- 查看迁移文件的应用状态(哪些已应用,哪些未应用)。
npx prisma migrate resolve --applied <migration-folder>- 手动标记某个迁移为已应用(解决迁移状态不一致问题)。
npx prisma version- 查看当前 prisma 相关包的版本信息。
常用指令
初始化
npx prisma init
schema.prisma 文件介绍
schema.prisma 文件是 Prisma 的配置文件,用于定义数据库连接和模型。它是一个 Prisma schema 文件,使用 Prisma 的 DSL(领域特定语言)来描述数据库架构和模型。
generator client {
provider = "prisma-client-js"
}
// 声明数据源
datasource db {
// 连接类型
provider = "postgresql"
// 链接字符串
url = env("DATABASE_URL") // env 函数会去读取 .env 文件中的 DATABASE_URL
}创建模型
在 schema.prisma 文件中,可以定义模型,每个模型代表数据库中的一个表。
model User {
// id 字段,类型为 Int,并且是主键,默认值是自增的
id Int @id @default(autoincrement())
// name 字段,类型为 String
name String
// age 字段,类型为 Int
age Int
// email 字段,类型为 String,并且是唯一约束
email String @unique
// createTime 字段,类型为 DateTime,默认值为当前时间
createTime DateTime @default(now()) @map("create_time") // 使用 map 函数将模型对象设置别名映射到数据库中的字段名(数据表中的字段名就使用 map 函数设置的参数)
// updateTime 字段,类型为 DateTime,默认值为当前时间,并且每次更新时自动更新
updateTime DateTime @updatedAt
// 通过 @@map 函数将模型对象设置别名映射到数据库中的表名(数据表中的名就使用 map 函数设置的参数)
@@map("users")
}上面方式定义创建时间和更新时间会有八小时的时差,使用以下方式进行创建
createdAt DateTime @default(dbgenerated("NOW()")) @db.DateTime
updatedAt DateTime @default(dbgenerated("NOW() ON UPDATE NOW()")) @db.Timestamp(0)将 prisma 模型映射到数据库创建表
npx prisma db push服务端使用 prisma
实例化 prisma 对象
const { PrismaClient } = require("@prisma/client")
const prisma = new PrismaClient()通过 prisma 实例进行一系列的数据库操作。
增删改查操作
增
语法:
prisma.<Model>.create({})
prisma.<Model>.createMany({})单条
prisma.user.create({
data: {
// 需要添加的数据
},
// 设置返回的字段,默认返回所有字段
select: {
id: true,
name: true
}
}).then(res => {
// res 返回生成的数据
})参数:
- select: 设置返回的字段,默认返回所有字段。
{select: {id: true, name: true}}
多条
prisma.user.createMany({
data: [
{...}
],
skipDuplicates: true // 跳过重复数据
}).then(res => {
// res 返回 { count: 2 } 总数
})参数:
- skipDuplicates: true: 跳过重复数据,原本如果数据是一个唯一值,那么会报错,设置这个参数后,如果数据重复,那么会跳过这条数据,不会报错。
删
单条
const result = await prisma.model.delete({
// 查询条件
where: {
id: 1
},
select: {
id: true
}
})返回的是一个被删除对象数据的参数,和新增数据返回参数一致
多条
const result = await prisma.model.deleteMany({
where: {
name: '张三'
}
})返回的参数是一个 {count: 1} 对象,表示删除了 1 条数据
改
单条
const result = await prisma.model.update({
where: {
id: 1
},
data: {
name: '李四'
},
select: {
id: true,
name: true
}
})返回的也是一个被修改对象数据的参数,和新增数据返回参数一致
多条
const result = await prisma.model.updateMany({
where: {
name: '张三'
},
data: {
name: '李四'
}
})返回的参数是一个 {count: 1} 对象,表示修改了 1 条数据
查
单条
const result = await prisma.model.findFirst({
where: {
id: 1
},
select: {
id: true,
name: true
}
})返回的也是一个被修改对象数据的参数,和新增数据返回参数一致
多条
const result = await prisma.model.findMany({
where: {
name: '张三'
},
select: {
id: true,
name: true
}
})返回的是一个数组,里面包含着多条查询到的数据
分页查询
- skip: 偏移量
- take: 查询数量
const result = await prisma.model.findMany({
where: {
name: '张三'
},
select: {
id: true,
name: true
},
skip: 0,
take: 10
})查询过滤条件
排序
- orderBy
const result = await prisma.model.findMany({
where: {
name: '张三'
},
select: {
id: true,
name: true
},
orderBy: {
id: 'asc' // asc 升序,desc 降序
}
})关联字段
一对一关系
如下示例,文章需要记录用户id
model User {
id Int @id @default(autoincrement())
name String
// profile 设置需要进行关联的表
// profile 模型名?
profile Article?
}
model Article {
id Int @id @default(autocrement())
title String
// user 可以自定义
// user User 表示 Article 模型中的一个字段 user,它的类型是 User。这意味着每篇文章 (Article) 都与一个用户 (User) 相关联。
// @relation(fields: [userId], references: [id]) 这是 Prisma 的关系注解,用于指定两个模型之间的关联方式。
// fields: [userId]: 这表示在 Article 模型中,userId 字段用于存储关联用户的 ID。
// references: [id]: 这表示 userId 字段的值应该与 User 模型中的 id 字段相匹配。
user User @relation(fields: [userId], references: [id])
userId Int @unique @map("user_id")
}User 是关系的持有者,而 Article 是关系的“被持有者”
关系的持有者 vs 被持有者
在 Prisma 中,关系的“持有者”是指定义关系的模型,而“被持有者”是指通过外键引用其他模型的模型。
- 持有者:定义了关系的方向(即谁“拥有”谁)。
- 被持有者:通过外键存储关联的 ID。
返回的数据结构大致如下:
默认是不返回关联字段的,需要加上一个配置
const user = await prisma.user.findFirst({
where: { id: "某个用户ID" },
include: { profile: true } // 必须显式包含关联字段才会返回
});{
id: 1,
name: "张三",
profile: {
// 是单个对象(而非数组)
id: 1,
bio: "热爱编程",
userId: 1
}
// 或如果没有关联的profile,则为:
// profile: null
}一对多关系
model User {
id String @id @default(cuid())
name String?
email String @unique
password String?
emailVerified DateTime?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}返回的数据结构大致如下:
默认是不返回关联字段的,需要加上一个配置
const user = await prisma.user.findFirst({
where: { id: "某个用户ID" },
include: { accounts: true } // 必须显式包含关联字段才会返回
});{
id: "用户ID",
name: "用户名",
email: "用户邮箱",
// 其他用户字段...
accounts: [
// 数组形式,包含所有关联的Account记录
{
id: "账户1ID",
userId: "关联的用户ID",
type: "oauth",
provider: "github",
providerAccountId: "github的用户ID",
// 其他Account字段...
},
{
id: "账户2ID",
userId: "关联的用户ID",
type: "email",
provider: "email",
providerAccountId: "用户邮箱",
// 其他Account字段...
}
// 可能有更多关联账户...
]
}onDelete: Cascade 说明:
onDelete: Cascade是一种级联删除配置,用于定义当主表(被引用的表)中的记录被删除时,从表(引用主表的表)中关联的记录应该如何处理。- 默认
onDelete: Restrict。
@@unique([provider, providerAccountId]) 说明:
@@unique是模型级别的复合唯一约束,写在模型的最后,用于保证多个字段的组合值唯一。