跳到主要内容

Null 和 undefined

警告

在 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-js"
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姓名电子邮件
1Nikolasnikolas@gmail.com
2Martinmartin@gmail.com
3sabin@gmail.com
4Tylertyler@gmail.com

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

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

Null

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

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

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

Undefined

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

const users = await prisma.user.findMany({
where: {
name: undefined,
},
})
显示查询结果
[
{
"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 值如何影响与数据库中单条记录交互或创建单条记录的查询行为。

警告

注意nullfindUnique() 查询中不是有效的过滤值。

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

Null

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

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

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

Undefined

如果改为在 name 列上使用 undefined 作为过滤值,查询将表现得如同根本没有向该列传递任何过滤条件一样

考虑以下查询

const user = await prisma.user.findFirst({
where: {
name: undefined,
},
})
显示查询结果
[
{
"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!
}

但是,如果您将 authorEmailauthorNamenull 值传递给 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' }
© . All rights reserved.