CRUD
本页介绍如何使用生成的 Prisma Client API 执行 CRUD 操作。CRUD 是以下首字母缩写:
有关每个方法的详细说明,请参阅 Prisma Client API 参考文档。
示例 schema
所有示例均基于以下 schema
展开查看示例 schema
- 关系型数据库
- MongoDB
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model ExtendedProfile {
id Int @id @default(autoincrement())
biography String
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
profileViews Int @default(0)
role Role @default(USER)
coinflips Boolean[]
posts Post[]
profile ExtendedProfile?
}
model Post {
id Int @id @default(autoincrement())
title String
published Boolean @default(true)
author User @relation(fields: [authorId], references: [id])
authorId Int
comments Json?
views Int @default(0)
likes Int @default(0)
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model ExtendedProfile {
id String @id @default(auto()) @map("_id") @db.ObjectId
biography String
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String?
email String @unique
profileViews Int @default(0)
role Role @default(USER)
coinflips Boolean[]
posts Post[]
profile ExtendedProfile?
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
title String
published Boolean @default(true)
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
comments Json?
views Int @default(0)
likes Int @default(0)
categories Category[]
}
model Category {
id String @id @default(auto()) @map("_id") @db.ObjectId
name String @unique
posts Post[]
}
enum Role {
USER
ADMIN
}
对于关系型数据库,使用 db push
命令将示例 schema 推送到您自己的数据库
npx prisma db push
对于 MongoDB,请确保您的数据形状一致并与 Prisma schema 中定义的模型匹配。
创建
创建单个记录
以下查询创建 (create()
) 一个包含两个字段的单个用户
const user = await prisma.user.create({
data: {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
},
})
用户的 id
是自动生成的,并且您的 schema 决定了哪些字段是必需的。
使用生成的类型创建单个记录
以下示例产生相同的结果,但在 create()
查询的上下文之外创建了一个名为 user
的 UserCreateInput
变量。在完成简单的检查(“帖子是否应包含在此 create()
查询中?”)后,将 user
变量传递给查询
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
let includePosts: boolean = false
let user: Prisma.UserCreateInput
// Check if posts should be included in the query
if (includePosts) {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
posts: {
create: {
title: 'Include this post!',
},
},
}
} else {
user = {
email: 'elsa@prisma.io',
name: 'Elsa Prisma',
}
}
// Pass 'user' object into query
const createUser = await prisma.user.create({ data: user })
}
main()
有关使用生成的类型的更多信息,请参阅:生成的类型。
创建多个记录
Prisma Client 在 2.20.0 及更高版本中以正式发布 (GA) 功能的形式支持批量插入。
以下 createMany()
查询创建多个用户并跳过任何重复项(email
必须是唯一的)
const createMany = await prisma.user.createMany({
data: [
{ name: 'Bob', email: 'bob@prisma.io' },
{ name: 'Bobo', email: 'bob@prisma.io' }, // Duplicate unique key!
{ name: 'Yewande', email: 'yewande@prisma.io' },
{ name: 'Angelique', email: 'angelique@prisma.io' },
],
skipDuplicates: true, // Skip 'Bobo'
})
{
count: 3
}
注意:使用 MongoDB、SQLServer 或 SQLite 时,不支持 skipDuplicates
。
createMany()
使用单个带多个值的 INSERT INTO
语句,这通常比每行一个单独的 INSERT
更有效率
BEGIN
INSERT INTO "public"."User" ("id","name","email","profileViews","role","coinflips","testing","city","country") VALUES (DEFAULT,$1,$2,$3,$4,DEFAULT,DEFAULT,DEFAULT,$5), (DEFAULT,$6,$7,$8,$9,DEFAULT,DEFAULT,DEFAULT,$10), (DEFAULT,$11,$12,$13,$14,DEFAULT,DEFAULT,DEFAULT,$15), (DEFAULT,$16,$17,$18,$19,DEFAULT,DEFAULT,DEFAULT,$20) ON CONFLICT DO NOTHING
COMMIT
SELECT "public"."User"."country", "public"."User"."city", "public"."User"."email", SUM("public"."User"."profileViews"), COUNT(*) FROM "public"."User" WHERE 1=1 GROUP BY "public"."User"."country", "public"."User"."city", "public"."User"."email" HAVING AVG("public"."User"."profileViews") >= $1 ORDER BY "public"."User"."country" ASC OFFSET $2
注意:在
$transaction
中包含多个create()
语句会导致生成多个INSERT
语句。
以下视频演示了如何使用 createMany()
和 faker.js 为数据库填充样本数据
创建记录并连接或创建相关记录
有关同时创建一条记录和一个或多个相关记录的信息,请参阅使用关系 > 嵌套写入。
创建并返回多个记录
此功能在 Prisma ORM 5.14.0 及更高版本中适用于 PostgreSQL、CockroachDB 和 SQLite。
您可以使用 createManyAndReturn()
来创建多个记录并返回结果对象。
const users = await prisma.user.createManyAndReturn({
data: [
{ name: 'Alice', email: 'alice@prisma.io' },
{ name: 'Bob', email: 'bob@prisma.io' },
],
})
使用 createManyAndReturn()
时,relationLoadStrategy: join
不可用。
读取
按 ID 或唯一标识符获取记录
以下查询按唯一标识符或 ID 返回单个记录 (findUnique()
)
// By unique identifier
const user = await prisma.user.findUnique({
where: {
email: 'elsa@prisma.io',
},
})
// By ID
const user = await prisma.user.findUnique({
where: {
id: 99,
},
})
如果您使用的是 MongoDB 连接器,并且底层 ID 类型是 ObjectId
,则可以使用该 ObjectId
的字符串表示形式
// By ID
const user = await prisma.user.findUnique({
where: {
id: '60d5922d00581b8f0062e3a8',
},
})
获取所有记录
以下 findMany()
查询返回所有 User
记录
const users = await prisma.user.findMany()
您还可以对结果进行分页。
获取符合特定条件的第一个记录
以下 findFirst()
查询返回最新创建的至少有一个帖子有超过 100 个赞的用户
- 按 ID 降序排序用户(最大 ID 在前)- 最大 ID 是最新的
- 返回按降序排列的第一个用户,该用户至少有一个帖子有超过 100 个赞
const findUser = await prisma.user.findFirst({
where: {
posts: {
some: {
likes: {
gt: 100,
},
},
},
},
orderBy: {
id: 'desc',
},
})
获取过滤后的记录列表
Prisma Client 支持对记录字段和相关记录字段进行过滤。
按单个字段值过滤
以下查询返回所有 email 以 "prisma.io"
结尾的 User
记录
const users = await prisma.user.findMany({
where: {
email: {
endsWith: 'prisma.io',
},
},
})
按多个字段值过滤
以下查询结合使用 运算符 返回姓名以 E
开头或至少有 1 次个人资料浏览量的管理员用户
const users = await prisma.user.findMany({
where: {
OR: [
{
name: {
startsWith: 'E',
},
},
{
AND: {
profileViews: {
gt: 0,
},
role: {
equals: 'ADMIN',
},
},
},
],
},
})
按相关记录字段值过滤
以下查询返回 email 以 prisma.io
结尾且至少有一个未发布的帖子 (some
) 的用户
const users = await prisma.user.findMany({
where: {
email: {
endsWith: 'prisma.io',
},
posts: {
some: {
published: false,
},
},
},
})
有关按相关字段值过滤的更多示例,请参阅使用关系。
选择字段子集
以下 findUnique()
查询使用 select
返回特定 User
记录的 email
和 name
字段
const user = await prisma.user.findUnique({
where: {
email: 'emma@prisma.io',
},
select: {
email: true,
name: true,
},
})
有关包含关系的更多信息,请参阅
选择相关记录字段子集
以下查询使用嵌套的 select
返回
- 用户的
email
- 每个帖子的
likes
字段
const user = await prisma.user.findUnique({
where: {
email: 'emma@prisma.io',
},
select: {
email: true,
posts: {
select: {
likes: true,
},
},
},
})
有关包含关系的更多信息,请参阅选择字段并包含关系。
选择 distinct 字段值
有关选择 distinct 字段值的信息,请参阅选择 distinct
。
包含相关记录
以下查询返回所有 ADMIN
用户并在结果中包含每个用户的帖子
const users = await prisma.user.findMany({
where: {
role: 'ADMIN',
},
include: {
posts: true,
},
})
有关包含关系的更多信息,请参阅选择字段并包含关系。
包含过滤后的关系列表
请参阅使用关系,了解如何组合 include
和 where
来获取过滤后的关系列表 - 例如,仅包含用户的已发布帖子。
更新
更新单个记录
以下查询使用 update()
按 email
查找并更新单个 User
记录
const updateUser = await prisma.user.update({
where: {
email: 'viola@prisma.io',
},
data: {
name: 'Viola the Magnificent',
},
})
更新多个记录
以下查询使用 updateMany()
更新所有包含 prisma.io
的 User
记录
const updateUsers = await prisma.user.updateMany({
where: {
email: {
contains: 'prisma.io',
},
},
data: {
role: 'ADMIN',
},
})
更新并返回多个记录
此功能在 Prisma ORM 6.2.0 及更高版本中适用于 PostgreSQL、CockroachDB 和 SQLite。
您可以使用 updateManyAndReturn()
来更新多个记录并返回结果对象。
const users = await prisma.user.updateManyAndReturn({
where: {
email: {
contains: 'prisma.io',
}
},
data: {
role: 'ADMIN'
}
})
使用 updateManyAndReturn()
时,relationLoadStrategy: join
不可用。
更新或创建记录
以下查询使用 upsert()
更新具有特定 email 地址的 User
记录,如果该 User
记录不存在则创建它
const upsertUser = await prisma.user.upsert({
where: {
email: 'viola@prisma.io',
},
update: {
name: 'Viola the Magnificent',
},
create: {
email: 'viola@prisma.io',
name: 'Viola the Magnificent',
},
})
从 4.6.0 版本开始,Prisma Client 在可能的情况下使用数据库原生 SQL 命令执行 upsert。了解更多。
Prisma Client 没有 findOrCreate()
查询。您可以使用 upsert()
作为一种变通方法。要使 upsert()
的行为类似于 findOrCreate()
方法,请向 upsert()
提供一个空的 update
参数。
将 upsert()
用作 findOrCreate()
的变通方法的一个限制是,upsert()
在 where
条件中仅接受唯一的模型字段。因此,如果 where
条件包含非唯一字段,则无法使用 upsert()
来模拟 findOrCreate()
。
更新数字字段
使用原子数字操作来基于当前值更新数字字段 - 例如,递增或相乘。以下查询将 views
和 likes
字段递增 1
const updatePosts = await prisma.post.updateMany({
data: {
views: {
increment: 1,
},
likes: {
increment: 1,
},
},
})
连接和断开相关记录
有关断开连接 (disconnect
) 和连接 (connect
) 相关记录的信息,请参阅使用关系。
删除
删除单个记录
以下查询使用 delete()
删除单个 User
记录
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
尝试删除一个或多个帖子关联的用户会导致错误,因为每个 Post
都需要一个作者 - 请参阅级联删除。
删除多个记录
以下查询使用 deleteMany()
删除所有 email
包含 prisma.io
的 User
记录
const deleteUsers = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
})
尝试删除一个或多个帖子关联的用户会导致错误,因为每个 Post
都需要一个作者 - 请参阅级联删除。
删除所有记录
以下查询使用 deleteMany()
删除所有 User
记录
const deleteUsers = await prisma.user.deleteMany({})
请注意,如果用户有任何相关记录(例如帖子),此查询将失败。在这种情况下,您需要先删除相关记录。
级联删除(删除相关记录)
以下查询使用 delete()
删除单个 User
记录
const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})
但是,示例 schema 包含 Post
和 User
之间的必需关系,这意味着您无法删除有关联帖子的用户
The change you are trying to make would violate the required relation 'PostToUser' between the `Post` and `User` models.
要解决此错误,您可以:
-
使关系可选
model Post {
id Int @id @default(autoincrement())
author User? @relation(fields: [authorId], references: [id])
authorId Int?
author User @relation(fields: [authorId], references: [id])
authorId Int
} -
在删除用户之前,将帖子的作者更改为另一个用户。
-
在事务中通过两个独立的查询删除用户及其所有帖子(所有查询必须成功)
const deletePosts = prisma.post.deleteMany({
where: {
authorId: 7,
},
})
const deleteUser = prisma.user.delete({
where: {
id: 7,
},
})
const transaction = await prisma.$transaction([deletePosts, deleteUser])
删除所有表中的所有记录
有时您想删除所有表中的所有数据,但保留实际的表。这在开发环境和测试期间特别有用。
以下展示了如何使用 Prisma Client 和 Prisma Migrate 删除所有表中的所有记录。
使用 deleteMany()
删除所有数据
当您知道应按什么顺序删除表时,可以使用 deleteMany
函数。这在 $transaction
中同步执行,并且可用于所有类型的数据库。
const deletePosts = prisma.post.deleteMany()
const deleteProfile = prisma.profile.deleteMany()
const deleteUsers = prisma.user.deleteMany()
// The transaction runs synchronously so deleteUsers must run last.
await prisma.$transaction([deleteProfile, deletePosts, deleteUsers])
✅ 优点
- 当您提前知道 schema 结构时,效果良好
- 同步删除每个表的数据
❌ 缺点
- 处理关系型数据库时,此函数的伸缩性不如更通用的解决方案好,后者会查找并
TRUNCATE
您的表,而不管其关系约束如何。请注意,使用 MongoDB 连接器时不存在此伸缩性问题。
注意:
$transaction
会在每个模型表上执行级联删除,因此必须按顺序调用它们。
使用原始 SQL / TRUNCATE
删除所有数据
如果您习惯使用原始 SQL,可以使用 $executeRawUnsafe
对表执行 TRUNCATE
查询。
在以下示例中,第一个选项卡显示了如何通过使用 $queryRaw
查找并映射到表,从而在单个查询中 TRUNCATE
所有表来在 Postgres 数据库上执行 TRUNCATE
。
第二个选项卡显示了在 MySQL 数据库上执行相同功能的示例。在这种情况下,必须在执行 TRUNCATE
之前移除约束,并在完成后重新设置。整个过程作为 $transaction
运行
- PostgreSQL
- MySQL
const tablenames = await prisma.$queryRaw<
Array<{ tablename: string }>
>`SELECT tablename FROM pg_tables WHERE schemaname='public'`
const tables = tablenames
.map(({ tablename }) => tablename)
.filter((name) => name !== '_prisma_migrations')
.map((name) => `"public"."${name}"`)
.join(', ')
try {
await prisma.$executeRawUnsafe(`TRUNCATE TABLE ${tables} CASCADE;`)
} catch (error) {
console.log({ error })
}
const transactions: PrismaPromise<any>[] = []
transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 0;`)
const tablenames = await prisma.$queryRaw<
Array<{ TABLE_NAME: string }>
>`SELECT TABLE_NAME from information_schema.TABLES WHERE TABLE_SCHEMA = 'tests';`
for (const { TABLE_NAME } of tablenames) {
if (TABLE_NAME !== '_prisma_migrations') {
try {
transactions.push(prisma.$executeRawUnsafe(`TRUNCATE ${TABLE_NAME};`))
} catch (error) {
console.log({ error })
}
}
}
transactions.push(prisma.$executeRaw`SET FOREIGN_KEY_CHECKS = 1;`)
try {
await prisma.$transaction(transactions)
} catch (error) {
console.log({ error })
}
✅ 优点
- 可伸缩
- 速度很快
❌ 缺点
- 无法撤消操作
- 将保留的 SQL 关键字用作表名可能会在尝试运行原始查询时导致问题
使用 Prisma Migrate 删除所有记录
如果您使用 Prisma Migrate,可以使用 migrate reset
,这将:
- 删除数据库
- 创建新数据库
- 应用迁移
- 用数据填充数据库
高级查询示例
创建深度嵌套的记录树
- 单个
User
- 两个新的相关
Post
记录 - 为每个帖子连接或创建
Category
const u = await prisma.user.create({
include: {
posts: {
include: {
categories: true,
},
},
},
data: {
email: 'emma@prisma.io',
posts: {
create: [
{
title: 'My first post',
categories: {
connectOrCreate: [
{
create: { name: 'Introductions' },
where: {
name: 'Introductions',
},
},
{
create: { name: 'Social' },
where: {
name: 'Social',
},
},
],
},
},
{
title: 'How to make cookies',
categories: {
connectOrCreate: [
{
create: { name: 'Social' },
where: {
name: 'Social',
},
},
{
create: { name: 'Cooking' },
where: {
name: 'Cooking',
},
},
],
},
},
],
},
},
})