Null 和 Undefined
在 Prisma ORM 5.20.0 之前,undefined
被视为一个特殊值,不会包含在生成的查询中。这种行为可能导致意外的结果和数据丢失。如果您使用的是旧版本的 Prisma ORM,我们强烈建议您更新到 5.20.0 或更高版本,以利用新的 strictUndefinedChecks
功能。
有关旧行为的文档,请参阅旧行为。
严格的 undefined 检查(预览功能)
Prisma ORM 5.20.0 引入了一项名为 strictUndefinedChecks
的新预览功能。此功能更改了 Prisma Client 处理 undefined
值的方式,提供了更好的保护,防止意外数据丢失或意外的查询行为。
启用严格的 undefined 检查
要启用此功能,请将以下内容添加到您的 Prisma 模式中
generator client {
provider = "prisma-client-js"
previewFeatures = ["strictUndefinedChecks"]
}
使用严格的 undefined 检查
启用此功能后
- 在查询中将字段显式设置为
undefined
将导致运行时错误。 - 要跳过查询中的字段,请使用新的
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
}
})
此新行为旨在成为 Prisma ORM 6 中的默认行为。
exactOptionalPropertyTypes
除了 strictUndefinedChecks
之外,我们还建议启用 TypeScript 编译器选项 exactOptionalPropertyTypes
。此选项强制执行可选属性必须完全匹配,这有助于捕获代码中 undefined
值的潜在问题。虽然 strictUndefinedChecks
将为无效的 undefined
使用引发运行时错误,但 exactOptionalPropertyTypes
将在构建过程中捕获这些问题。
在TypeScript 文档中了解有关 exactOptionalPropertyTypes
的更多信息。
反馈
与往常一样,我们欢迎您对此功能提供反馈。请在此预览功能的 GitHub 讨论中分享您的想法和建议。
旧行为
如果您使用的是 Prisma ORM 5.19.1 或更早版本,则本节内容相关。
Prisma Client 区分 null
和 undefined
null
是一个**值**undefined
表示**不执行任何操作**
以下数据表示一个 User
表格。此数据集将用于下面所有的示例
id | name | |
---|---|---|
1 | Nikolas | [email protected] |
2 | Martin | [email protected] |
3 | empty | [email protected] |
4 | Tyler | [email protected] |
影响多个记录的查询中的null
和 undefined
本节将介绍 undefined
和 null
值如何影响与数据库中多个记录交互或创建多个记录的查询的行为。
Null
考虑以下 Prisma Client 查询,该查询查找所有 name
值与提供的 null
值匹配的用户
const users = await prisma.user.findMany({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "[email protected]"
}
]
由于 null
被提供为 name
列的过滤器,因此 Prisma Client 将生成一个查询,该查询搜索 User
表格中 name
列为空的所有记录。
Undefined
现在考虑您使用 undefined
作为 name
列上的过滤器值的场景
const users = await prisma.user.findMany({
where: {
name: undefined,
},
})
[
{
"id": 1,
"name": "Nikolas",
"email": "[email protected]"
},
{
"id": 2,
"name": "Martin",
"email": "[email protected]"
},
{
"id": 3,
"name": null,
"email": "[email protected]"
},
{
"id": 4,
"name": "Tyler",
"email": "[email protected]"
}
]
在过滤器中使用 undefined
作为值实际上告诉 Prisma Client 您已决定不为该列定义过滤器。
编写上述查询的等效方法是
const users = await prisma.user.findMany()
此查询将从 User
表格中选择每一行。
注意:在 Prisma Client 查询的参数对象中,将 undefined
用作任何键的值,会导致 Prisma ORM 就像该键根本未提供一样。
尽管本节的示例重点关注 findMany
函数,但相同的概念适用于任何可以影响多个记录的函数,例如 updateMany
和 deleteMany
。
影响一个记录的查询中的null
和 undefined
本节将介绍 undefined
和 null
值如何影响与数据库中单个记录交互或创建单个记录的查询的行为。
注意:null
不是 findUnique()
查询中的有效过滤器值。
在影响单个记录的查询的过滤器条件中使用 null
和 undefined
时的查询行为与上一节中描述的行为非常相似。
Null
考虑以下查询,其中 null
用于过滤 name
列
const user = await prisma.user.findFirst({
where: {
name: null,
},
})
[
{
"id": 3,
"name": null,
"email": "[email protected]"
}
]
由于 null
被用作 name
列的过滤器,因此 Prisma Client 将生成一个查询,该查询搜索 User
表格中 name
值为空的第一条记录。
未定义
如果在 name
列上使用 undefined
作为过滤器值,查询将表现得好像根本没有向该列传递任何过滤条件。
考虑以下查询
const user = await prisma.user.findFirst({
where: {
name: undefined,
},
})
[
{
"id": 1,
"name": "Nikolas",
"email": "[email protected]"
}
]
在这种情况下,查询将返回数据库中的第一条记录。
表示上述查询的另一种方法是
const user = await prisma.user.findFirst()
虽然本节的示例侧重于 findFirst
函数,但相同概念适用于影响单个记录的任何函数。
GraphQL 解析器中的 null
和 undefined
对于此示例,请考虑一个基于以下 Prisma 模式构建的数据库
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
在以下更新用户的 GraphQL 变异中,authorEmail
和 name
都接受 null
。从 GraphQL 的角度来看,这意味着这些字段是可选的
type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}
但是,如果您将 authorEmail
或 authorName
的 null
值传递给 Prisma Client,将会发生以下情况
- 如果
args.authorEmail
为null
,则查询将失败。email
不接受null
。 - 如果
args.authorName
为null
,则 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
,请将 email
和 name
的值设置为 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
},
})
},
null
和 undefined
对条件语句的影响
使用条件语句进行过滤存在一些注意事项,这些注意事项可能会产生意外的结果。在使用条件语句进行过滤时,您可能期望得到一个结果,但考虑到 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
。运行此查询时,不会返回任何数据。
这与 AND
和 NOT
运算符形成对比,如果传递 undefined
值,这两个运算符都将返回所有用户。
这是因为将
undefined
值传递给AND
或NOT
运算符与完全不传递任何值相同,这意味着示例中的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: '[email protected]', name: 'Emelie' }
const users = await prisma.user.findMany({
where: {
NOT: [
{
email: {
contains: formData.email,
},
},
],
},
})
// returns: { id: 1, email: '[email protected]', name: 'Emelie' }