跳到主要内容

CRUD

本页介绍如何使用生成的 Prisma Client API 执行 CRUD 操作。CRUD 是以下首字母缩写:

有关每个方法的详细说明,请参阅 Prisma Client API 参考文档

示例 schema

所有示例均基于以下 schema

展开查看示例 schema
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
}

对于关系型数据库,使用 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() 查询的上下文之外创建了一个名为 userUserCreateInput 变量。在完成简单的检查(“帖子是否应包含在此 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 个赞的用户

  1. 按 ID 降序排序用户(最大 ID 在前)- 最大 ID 是最新的
  2. 返回按降序排列的第一个用户,该用户至少有一个帖子有超过 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 记录的 emailname 字段

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,
},
})
显示查询结果

有关包含关系的更多信息,请参阅选择字段并包含关系

包含过滤后的关系列表

请参阅使用关系,了解如何组合 includewhere 来获取过滤后的关系列表 - 例如,仅包含用户的已发布帖子。

更新

更新单个记录

以下查询使用 update()email 查找并更新单个 User 记录

const updateUser = await prisma.user.update({
where: {
email: 'viola@prisma.io',
},
data: {
name: 'Viola the Magnificent',
},
})
显示查询结果

更新多个记录

以下查询使用 updateMany() 更新所有包含 prisma.ioUser 记录

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()

更新数字字段

使用原子数字操作基于当前值更新数字字段 - 例如,递增或相乘。以下查询将 viewslikes 字段递增 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.ioUser 记录

const deleteUsers = await prisma.user.deleteMany({
where: {
email: {
contains: 'prisma.io',
},
},
})

尝试删除一个或多个帖子关联的用户会导致错误,因为每个 Post 都需要一个作者 - 请参阅级联删除

删除所有记录

以下查询使用 deleteMany() 删除所有 User 记录

const deleteUsers = await prisma.user.deleteMany({})

请注意,如果用户有任何相关记录(例如帖子),此查询将失败。在这种情况下,您需要先删除相关记录

注意

2.26.0 及更高版本中,可以使用预览特性引用操作进行级联删除。

以下查询使用 delete() 删除单个 User 记录

const deleteUser = await prisma.user.delete({
where: {
email: 'bert@prisma.io',
},
})

但是,示例 schema 包含 PostUser 之间的必需关系,这意味着您无法删除有关联帖子的用户

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 运行

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 })
}

优点

  • 可伸缩
  • 速度很快

缺点

  • 无法撤消操作
  • 将保留的 SQL 关键字用作表名可能会在尝试运行原始查询时导致问题

使用 Prisma Migrate 删除所有记录

如果您使用 Prisma Migrate,可以使用 migrate reset,这将:

  1. 删除数据库
  2. 创建新数据库
  3. 应用迁移
  4. 用数据填充数据库

高级查询示例

创建深度嵌套的记录树

  • 单个 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',
},
},
],
},
},
],
},
},
})