跳至主要内容

CRUD

此页面描述了如何使用生成的 Prisma Client API 执行 CRUD 操作。CRUD 是一个首字母缩写词,代表

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

示例模式

所有示例都基于以下模式

展开以查看示例模式
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 命令将示例模式推送到您自己的数据库

npx prisma db push

对于**MongoDB**,请确保您的数据格式统一,并与 Prisma 模式中定义的模型匹配。

创建

创建单个记录

以下查询创建 (create()) 一个具有两个字段的单个用户

const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'Elsa Prisma',
},
})
显示查询结果

用户的 id 是自动生成的,并且您的模式确定哪些字段是必填字段

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

以下示例产生相同的结果,但创建了一个名为 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: '[email protected]',
name: 'Elsa Prisma',
posts: {
create: {
title: 'Include this post!',
},
},
}
} else {
user = {
email: '[email protected]',
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: '[email protected]' },
{ name: 'Bobo', email: '[email protected]' }, // Duplicate unique key!
{ name: 'Yewande', email: '[email protected]' },
{ name: 'Angelique', email: '[email protected]' },
],
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 使用示例数据填充数据库

有关同时创建记录和一个或多个相关记录的信息,请参阅使用关系 > 嵌套写入

创建并返回多个记录

信息

此功能在 PostgreSQL、CockroachDB 和 SQLite 的 Prisma ORM 5.14.0 及更高版本中可用。

您可以使用 createManyAndReturn() 创建多个记录并返回结果对象。

const users = await prisma.user.createManyAndReturn({
data: [
{ name: 'Alice', email: '[email protected]' },
{ name: 'Bob', email: '[email protected]' },
],
})
显示查询结果
警告

使用 createManyAndReturn() 时,relationLoadStrategy: join 不可用。

读取

按 ID 或唯一标识符获取记录

以下查询按唯一标识符或 ID 返回单个记录 (findUnique())

// By unique identifier
const user = await prisma.user.findUnique({
where: {
email: '[email protected]',
},
})

// 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. 返回 ID 降序排列的第一个用户,该用户至少有一篇帖子获得超过 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: '[email protected]',
},
select: {
email: true,
name: true,
},
})
显示查询结果

有关包含关联的更多信息,请参阅

以下查询使用嵌套的 select 返回

  • 用户的 email
  • 每篇帖子的 likes 字段
const user = await prisma.user.findUnique({
where: {
email: '[email protected]',
},
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: '[email protected]',
},
data: {
name: 'Viola the Magnificent',
},
})
显示查询结果

更新多条记录

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

const updateUsers = await prisma.user.updateMany({
where: {
email: {
contains: 'prisma.io',
},
},
data: {
role: 'ADMIN',
},
})
显示查询结果

更新创建记录

以下查询使用upsert() 更新具有特定电子邮件地址的 User 记录,如果该 User 记录不存在则创建该记录

const upsertUser = await prisma.user.upsert({
where: {
email: '[email protected]',
},
update: {
name: 'Viola the Magnificent',
},
create: {
email: '[email protected]',
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: '[email protected]',
},
})

尝试删除一个或多个帖子的用户会导致错误,因为每个 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({})

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

警告

2.26.0及更高版本中,可以使用预览功能参照操作执行级联删除。

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

const deleteUser = await prisma.user.delete({
where: {
email: '[email protected]',
},
})

但是,示例架构在 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])

优点

  • 当您提前知道架构结构时效果很好
  • 同步删除每个表的 data

缺点

  • 在处理关系数据库时,此函数的扩展性不如具有更通用的解决方案,该解决方案查找并 TRUNCATE 您的表,而不管其关系约束如何。请注意,使用 MongoDB 连接器时,此扩展性问题不适用。

注意$transaction 对每个模型的表执行级联删除,因此必须按顺序调用它们。

使用原始 SQL/TRUNCATE 删除所有数据

如果您习惯使用原始 SQL,则可以使用$executeRawUnsafe对表执行 TRUNCATE 查询。

在以下示例中,第一个选项卡显示了如何通过使用映射到表并使用单个查询 TRUNCATE 所有表的 $queryRaw 查找来对 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: '[email protected]',
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',
},
},
],
},
},
],
},
},
})