跳到主要内容

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 决定了哪些字段是必需的

使用生成的类型创建单个记录

以下示例产生相同的结果,但创建了一个名为 userUserCreateInput 变量,其位于 create() 查询的上下文之外。在完成简单检查(“此 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 是最新的。
  2. 返回降序排列的第一个用户,该用户至少有一篇文章的点赞数超过 100。
const findUser = await prisma.user.findFirst({
where: {
posts: {
some: {
likes: {
gt: 100,
},
},
},
},
orderBy: {
id: 'desc',
},
})

获取过滤后的记录列表

Prisma Client 支持对记录字段和相关记录字段进行过滤

按单个字段值过滤

以下查询返回所有邮箱以 "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',
},
},
},
],
},
})

以下查询返回邮箱以 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

以下查询返回所有 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() 更新具有特定邮箱地址的 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 查找来对 Postgres 数据库执行 TRUNCATE,该查找会遍历表并在单个查询中 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',
},
},
],
},
},
],
},
},
})
© . All rights reserved.