跳至主要内容

空和未定义

警告

在 Prisma ORM 中,如果将 undefined 作为值传递,它将不包含在生成的查询中。此行为可能导致意外结果和数据丢失。为防止这种情况,我们强烈建议更新到 5.20.0 或更高版本,以利用下面描述的新 strictUndefinedChecks 预览功能。

有关当前行为(不带 strictUndefinedChecks 预览功能)的文档,请参阅当前行为

严格的 undefined 检查(预览功能)

Prisma ORM 5.20.0 引入了一项名为 strictUndefinedChecks 的新预览功能。此功能改变了 Prisma Client 处理 undefined 值的方式,从而更好地防止意外数据丢失或意外的查询行为。

启用严格的 undefined 检查

要启用此功能,请将以下内容添加到您的 Prisma schema 中

generator client {
provider = "prisma-client"
output = "./generated"
previewFeatures = ["strictUndefinedChecks"]
}

使用严格的 undefined 检查

启用此功能后

  1. 在查询中将字段显式设置为 undefined 将导致运行时错误。
  2. 要跳过查询中的字段,请使用新的 Prisma.skip 符号而不是 undefined

使用示例

// This will throw an error
prisma.user.create({
data: {
name: 'Alice',
email: undefined // Error: Cannot explicitly use undefined here
}
})

// Use `Prisma.skip` (a symbol provided by Prisma) to omit a field
prisma.user.create({
data: {
name: 'Alice',
email: Prisma.skip // This field will be omitted from the query
}
})

此更改有助于防止意外删除或更新,例如

// Before: This would delete all users
prisma.user.deleteMany({
where: {
id: undefined
}
})

// After: This will throw an error
Invalid \`prisma.user.deleteMany()\` invocation in
/client/tests/functional/strictUndefinedChecks/test.ts:0:0
XX })
XX
XX test('throws on undefined input field', async () => {
XX const result = prisma.user.deleteMany({
where: {
id: undefined
~~~~~~~~~
}
})
Invalid value for argument \`where\`: explicitly \`undefined\` values are not allowed."

迁移路径

要迁移现有代码

// Before
let optionalEmail: string | undefined

prisma.user.create({
data: {
name: 'Alice',
email: optionalEmail
}
})

// After
prisma.user.create({
data: {
name: 'Alice',
email: optionalEmail ?? Prisma.skip
}
})

exactOptionalPropertyTypes

除了 strictUndefinedChecks,我们还建议启用 TypeScript 编译器选项 exactOptionalPropertyTypes。此选项强制可选属性必须完全匹配,这有助于捕获代码中 undefined 值的潜在问题。虽然 strictUndefinedChecks 会对无效的 undefined 使用引发运行时错误,但 exactOptionalPropertyTypes 将在构建过程中捕获这些问题。

TypeScript 文档中了解更多关于 exactOptionalPropertyTypes 的信息。

反馈

一如既往,我们欢迎您对该功能的反馈。请在此预览功能的 GitHub 讨论中分享您的想法和建议。

当前行为

Prisma Client 区分 nullundefined

  • null 是一个
  • undefined 意味着什么都不做

以下数据表示一个 User 表。这组数据将用于以下所有示例中

id名称电子邮件
1尼古拉nikolas@gmail.com
2马丁martin@gmail.com
3sabin@gmail.com
4泰勒tyler@gmail.com

影响多个记录的查询中的 nullundefined

本节将介绍 undefinednull 值如何影响与数据库中多个记录交互或创建多个记录的查询行为。

Null

考虑以下 Prisma Client 查询,该查询搜索所有 name 值与提供的 null 值匹配的用户

const users = await prisma.user.findMany({
where: {
name: null,
},
})
显示query结果
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]

由于提供了 null 作为 name 列的过滤器,Prisma Client 将生成一个查询,搜索 User 表中所有 name 列为的记录。

Undefined

现在考虑以下场景:您使用 undefined 作为 name 列上的过滤值运行相同的查询

const users = await prisma.user.findMany({
where: {
name: undefined,
},
})
显示query结果
[
{
"id": 1,
"name": "Nikolas",
"email": "nikolas@gmail.com"
},
{
"id": 2,
"name": "Martin",
"email": "martin@gmail.com"
},
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
},
{
"id": 4,
"name": "Tyler",
"email": "tyler@gmail.com"
}
]

在过滤器中使用 undefined 作为值,实际上是在告诉 Prisma Client 您已决定不为该列定义过滤器

编写上述查询的等效方法是

const users = await prisma.user.findMany()

此查询将从 User 表中选择所有行。

信息

在 Prisma Client 查询参数对象的任何键中使用 undefined 作为值,将导致 Prisma ORM 的行为就好像根本没有提供该键一样。

尽管本节的示例侧重于 findMany 函数,但相同的概念适用于任何可以影响多个记录的函数,例如 updateManydeleteMany

影响一个记录的查询中的 nullundefined

本节将介绍 undefinednull 值如何影响与数据库中单个记录交互或创建单个记录的查询行为。

警告

null 不是 findUnique() 查询中的有效过滤值。

在影响单个记录的查询的过滤条件中使用 nullundefined 时的查询行为与上一节中描述的行为非常相似。

Null

考虑以下查询,其中 null 用于过滤 name

const user = await prisma.user.findFirst({
where: {
name: null,
},
})
显示query结果
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]

由于 null 用作 name 列上的过滤器,Prisma Client 将生成一个查询,搜索 User 表中 name 值为的第一条记录。

Undefined

如果使用 undefined 作为 name 列上的过滤值,则查询将表现为根本没有向该列传递过滤条件

考虑以下查询

const user = await prisma.user.findFirst({
where: {
name: undefined,
},
})
显示query结果
[
{
"id": 1,
"name": "Nikolas",
"email": "nikolas@gmail.com"
}
]

在这种情况下,查询将返回数据库中的第一条记录。

表示上述查询的另一种方法是

const user = await prisma.user.findFirst()

尽管本节的示例侧重于 findFirst 函数,但相同的概念适用于任何影响单个记录的函数。

GraphQL 解析器中的 nullundefined

对于此示例,考虑一个基于以下 Prisma schema 的数据库

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}

在以下更新用户的 GraphQL 突变中,authorEmailname 都接受 null。从 GraphQL 的角度来看,这意味着字段是可选的

type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}

但是,如果您将 null 值用于 authorEmailauthorName 传递给 Prisma Client,则会发生以下情况

  • 如果 args.authorEmailnull,则查询将失败email 不接受 null
  • 如果 args.authorNamenull,Prisma Client 会将 name 的值更改为 null。这可能不是您希望更新工作的方式。
updateUser: (parent, args, ctx: Context) => {
return ctx.prisma.user.update({
where: { id: Number(args.id) },
data: {
email: args.authorEmail, // email cannot be null
name: args.authorName // name set to null - potentially unwanted behavior
},
})
},

相反,如果输入值为 null,则将 emailname 的值设置为 undefined。这样做与根本不更新字段相同

updateUser: (parent, args, ctx: Context) => {
return ctx.prisma.user.update({
where: { id: Number(args.id) },
data: {
email: args.authorEmail != null ? args.authorEmail : undefined, // If null, do nothing
name: args.authorName != null ? args.authorName : undefined // If null, do nothing
},
})
},

nullundefined 对条件语句的影响

使用条件进行过滤时存在一些注意事项,可能会产生意外结果。在使用条件进行过滤时,您可能会期望一个结果,但鉴于 Prisma Client 处理可空值的方式,您会收到另一个结果。

下表提供了不同运算符如何处理 0、1 和 n 个过滤器的高级概述。

运算符0 个过滤器1 个过滤器n 个过滤器
OR返回空列表验证单个过滤器验证所有过滤器
AND返回所有项目验证单个过滤器验证所有过滤器
NOT返回所有项目验证单个过滤器验证所有过滤器

此示例展示了 undefined 参数如何影响使用 OR 运算符的查询返回的结果。

interface FormData {
name: string
email?: string
}

const formData: FormData = {
name: 'Emelie',
}

const users = await prisma.user.findMany({
where: {
OR: [
{
email: {
contains: formData.email,
},
},
],
},
})

// returns: []

该查询从 formData 对象接收过滤器,该对象包含一个可选的 email 属性。在此示例中,email 属性的值为 undefined。运行此查询时,不会返回任何数据。

这与 ANDNOT 运算符相反,如果您传入 undefined 值,它们都将返回所有用户。

这是因为将 undefined 值传递给 ANDNOT 运算符与根本不传递任何内容相同,这意味着示例中的 findMany 查询将不带任何过滤器运行并返回所有用户。

interface FormData {
name: string
email?: string
}

const formData: FormData = {
name: 'Emelie',
}

const users = await prisma.user.findMany({
where: {
AND: [
{
email: {
contains: formData.email,
},
},
],
},
})

// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' }

const users = await prisma.user.findMany({
where: {
NOT: [
{
email: {
contains: formData.email,
},
},
],
},
})

// returns: { id: 1, email: 'ems@boop.com', name: 'Emelie' }
© . This site is unofficial and not affiliated with Prisma Data, Inc.