空且未定义
在 Prisma ORM 中,如果 undefined
作为值传递,则它不会包含在生成的查询中。此行为可能导致意外结果和数据丢失。为了避免这种情况,我们强烈建议你更新到 5.20.0 或更高版本,以利用新的 strictUndefinedChecks
预览功能(如下所述)。
¥In Prisma ORM, if undefined
is passed as a value, it is not included in the generated query. This behavior can lead to unexpected results and data loss. In order to prevent this, we strongly recommend updating to version 5.20.0 or later to take advantage of the new strictUndefinedChecks
Preview feature, described below.
有关当前行为(不包含 strictUndefinedChecks
预览功能)的文档,请参阅 当前行为。
¥For documentation on the current behavior (without the strictUndefinedChecks
Preview feature) see current behavior.
严格未定义检查(预览功能)
¥Strict undefined checks (Preview feature)
Prisma ORM 5.20.0 引入了一项名为 strictUndefinedChecks
的新预览功能。此功能改变了 Prisma Client 处理 undefined
值的方式,从而更好地防止意外数据丢失或意外查询行为。
¥Prisma ORM 5.20.0 introduces a new Preview feature called strictUndefinedChecks
. This feature changes how Prisma Client handles undefined
values, offering better protection against accidental data loss or unintended query behavior.
启用严格未定义检查
¥Enabling strict undefined checks
要启用此功能,请将以下内容添加到 Prisma 模式中:
¥To enable this feature, add the following to your Prisma schema:
generator client {
provider = "prisma-client-js"
previewFeatures = ["strictUndefinedChecks"]
}
使用严格未定义检查
¥Using strict undefined checks
启用此功能后:
¥When this feature is enabled:
-
在查询中明确将字段设置为
undefined
将导致运行时错误。¥Explicitly setting a field to
undefined
in a query will cause a runtime error. -
要跳过查询中的字段,请使用新的
Prisma.skip
符号而不是undefined
。¥To skip a field in a query, use the new
Prisma.skip
symbol instead ofundefined
.
示例用法:
¥Example usage:
// 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
}
})
此更改有助于防止意外删除或更新,例如:
¥This change helps prevent accidental deletions or updates, such as:
// 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."
迁移路径
¥Migration path
要迁移现有代码:
¥To migrate existing code:
// 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
会在构建过程中捕获这些问题。
¥In addition to strictUndefinedChecks
, we also recommend enabling the TypeScript compiler option exactOptionalPropertyTypes
. This option enforces that optional properties must match exactly, which can help catch potential issues with undefined
values in your code. While strictUndefinedChecks
will raise runtime errors for invalid undefined
usage, exactOptionalPropertyTypes
will catch these issues during the build process.
在 TypeScript 文档 中了解有关 exactOptionalPropertyTypes
的更多信息。
¥Learn more about exactOptionalPropertyTypes
in the TypeScript documentation.
反馈
¥Feedback
与往常一样,我们欢迎你对此功能的反馈。请在 此预览功能的 GitHub 讨论 中分享你的想法和建议。
¥As always, we welcome your feedback on this feature. Please share your thoughts and suggestions in the GitHub discussion for this Preview feature.
当前行为
¥current behavior
Prisma 客户端区分 null
和 undefined
:
¥Prisma Client differentiates between null
and undefined
:
-
null
是一个值¥
null
is a value -
undefined
表示不执行任何操作¥
undefined
means do nothing
这在 具有 GraphQL 上下文的 Prisma ORM,其中 null
和 undefined
可以互换 中尤为重要。
¥This is particularly important to account for in a Prisma ORM with GraphQL context, where null
and undefined
are interchangeable.
下面的数据代表 User
表。这组数据将用于以下所有示例:
¥The data below represents a User
table. This set of data will be used in all of the examples below:
id | name | |
---|---|---|
1 | 尼古拉斯 | nikolas@gmail.com |
2 | 马丁 | martin@gmail.com |
3 | empty | sabin@gmail.com |
4 | 泰勒 | tyler@gmail.com |
影响多条记录的查询中的 null
和 undefined
¥null
and undefined
in queries that affect many records
本节将介绍 undefined
和 null
值如何影响与数据库交互或在数据库中创建多个记录的查询的行为。
¥This section will cover how undefined
and null
values affect the behavior of queries that interact with or create multiple records in a database.
Null
考虑以下 Prisma 客户端查询,该查询搜索 name
值与提供的 null
值匹配的所有用户:
¥Consider the following Prisma Client query which searches for all users whose name
value matches the provided null
value:
const users = await prisma.user.findMany({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]
由于提供了 null
作为 name
列的过滤器,Prisma 客户端将生成一个查询,搜索 User
表中 name
列为空的所有记录。
¥Because null
was provided as the filter for the name
column, Prisma Client will generate a query that searches for all records in the User
table whose name
column is empty.
未定义
¥Undefined
现在考虑这样的场景:你使用 undefined
作为 name
列上的筛选器值运行相同的查询:
¥Now consider the scenario where you run the same query with undefined
as the filter value on the name
column:
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 客户端你决定不为该列定义过滤器。
¥Using undefined
as a value in a filter essentially tells Prisma Client you have decided not to define a filter for that column.
编写上述查询的等效方法是:
¥An equivalent way to write the above query would be:
const users = await prisma.user.findMany()
此查询将从 User
表中选择每一行。
¥This query will select every row from the User
table.
注意:使用 undefined
作为 Prisma 客户端查询参数对象中任何键的值将导致 Prisma ORM 的行为就像根本未提供该键一样。
¥Note: Using undefined
as the value of any key in a Prisma Client query's parameter object will cause Prisma ORM to act as if that key was not provided at all.
尽管本节的示例重点关注 findMany
函数,但相同的概念适用于可以影响多个记录的任何函数,例如 updateMany
和 deleteMany
。
¥Although this section's examples focused on the findMany
function, the same concepts apply to any function that can affect multiple records, such as updateMany
and deleteMany
.
影响一条记录的查询中的 null
和 undefined
¥null
and undefined
in queries that affect one record
本节将介绍 undefined
和 null
值如何影响与数据库交互或在数据库中创建单个记录的查询的行为。
¥This section will cover how undefined
and null
values affect the behavior of queries that interact with or create a single record in a database.
注意:null
不是 findUnique()
查询中的有效过滤器值。
¥Note: null
is not a valid filter value in a findUnique()
query.
在影响单个记录的查询的过滤条件中使用 null
和 undefined
时的查询行为与上一节中描述的行为非常相似。
¥The query behavior when using null
and undefined
in the filter criteria of a query that affects a single record is very similar to the behaviors described in the previous section.
Null
考虑以下查询,其中 null
用于过滤 name
列:
¥Consider the following query where null
is used to filter the name
column:
const user = await prisma.user.findFirst({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "sabin@gmail.com"
}
]
由于 null
被用作 name
列的过滤器,Prisma 客户端将生成一个查询,搜索 User
表中 name
值为空的第一条记录。
¥Because null
was used as the filter on the name
column, Prisma Client will generate a query that searches for the first record in the User
table whose name
value is empty.
未定义
¥Undefined
如果 undefined
用作 name
列上的筛选器值,则查询将表现为根本没有筛选条件传递到该列。
¥If undefined
is used as the filter value on the name
column instead, the query will act as if no filter criteria was passed to that column at all.
考虑下面的查询:
¥Consider the query below:
const user = await prisma.user.findFirst({
where: {
name: undefined,
},
})
[
{
"id": 1,
"name": "Nikolas",
"email": "nikolas@gmail.com"
}
]
在这种情况下,查询将返回数据库中的第一条记录。
¥In this scenario, the query will return the very first record in the database.
表示上述查询的另一种方法是:
¥Another way to represent the above query is:
const user = await prisma.user.findFirst()
尽管本节的示例重点关注 findFirst
函数,但相同的概念适用于影响单个记录的任何函数。
¥Although this section's examples focused on the findFirst
function, the same concepts apply to any function that affects a single record.
GraphQL 解析器中的 null
和 undefined
¥null
and undefined
in a GraphQL resolver
对于此示例,请考虑基于以下 Prisma 架构的数据库:
¥For this example, consider a database based on the following Prisma schema:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
在以下更新用户的 GraphQL 突变中,authorEmail
和 name
都接受 null
。从 GraphQL 的角度来看,这意味着字段是可选的:
¥In the following GraphQL mutation that updates a user, both authorEmail
and name
accept null
. From a GraphQL perspective, this means that fields are optional:
type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}
但是,如果你将 authorEmail
或 authorName
的 null
值传递给 Prisma 客户端,则会发生以下情况:
¥However, if you pass null
values for authorEmail
or authorName
on to Prisma Client, the following will happen:
-
如果
args.authorEmail
是null
,则查询失败。email
不接受null
。¥If
args.authorEmail
isnull
, the query will fail.email
does not acceptnull
. -
如果
args.authorName
是null
,Prisma 客户端会将name
的值更改为null
。这可能不是你希望更新的工作方式。¥If
args.authorName
isnull
, Prisma Client changes the value ofname
tonull
. This is probably not how you want an update to work.
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
,则将 email
和 name
的值设置为 undefined
。这样做与根本不更新字段相同:
¥Instead, set the value of email
and name
to undefined
if the input value is null
. Doing this is the same as not updating the field at all:
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
},
})
},
null
和 undefined
对条件句的影响
¥The effect of null
and undefined
on conditionals
使用条件过滤有一些注意事项,可能会产生意外的结果。当使用条件过滤时,你可能期望一个结果,但考虑到 Prisma Client 如何处理可为空值,你可能会收到另一个结果。
¥There are some caveats to filtering with conditionals which might produce unexpected results. When filtering with conditionals you might expect one result but receive another given how Prisma Client treats nullable values.
下表提供了不同运算符如何处理 0、1 和 n
过滤器的高级概述。
¥The following table provides a high-level overview of how the different operators handle 0, 1 and n
filters.
运算符 | 0 个过滤器 | 1 个过滤器 | n 个过滤器 |
---|---|---|---|
OR | 返回空列表 | 验证单个过滤器 | 验证所有过滤器 |
AND | 退回所有物品 | 验证单个过滤器 | 验证所有过滤器 |
NOT | 退回所有物品 | 验证单个过滤器 | 验证所有过滤器 |
此示例显示 undefined
参数如何影响使用 OR
运算符的查询返回的结果。
¥This example shows how an undefined
parameter impacts the results returned by a query that uses the OR
operator.
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 对象接收过滤器,其中包括可选的电子邮件属性。在本例中,电子邮件属性的值为 undefined
。运行此查询时,不会返回任何数据。
¥The query receives filters from a formData object, which includes an optional email property. In this instance, the value of the email property is undefined
. When this query is run no data is returned.
这与 AND
和 NOT
运算符形成对比,如果你传入 undefined
值,它们都会返回所有用户。
¥This is in contrast to the AND
and NOT
operators, which will both return all the users
if you pass in an undefined
value.
这是因为将
undefined
值传递给AND
或NOT
运算符与不传递任何内容相同,这意味着示例中的findMany
查询将在没有任何过滤器的情况下运行并返回所有用户。¥This is because passing an
undefined
value to anAND
orNOT
operator is the same as passing nothing at all, meaning thefindMany
query in the example will run without any filters and return all the users.
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' }