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 检查
当此功能启用时
- 在查询中将字段显式设置为
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
}
})
exactOptionalPropertyTypes
除了 strictUndefinedChecks
之外,我们还建议启用 TypeScript 编译器选项 exactOptionalPropertyTypes
。此选项强制可选属性必须精确匹配,这有助于捕获代码中 undefined
值潜在问题。虽然 strictUndefinedChecks
会在 undefined
用法无效时引发运行时错误,但 exactOptionalPropertyTypes
将在构建过程中捕获这些问题。
在 TypeScript 文档中了解更多关于 exactOptionalPropertyTypes
的信息。
反馈
一如既往,我们欢迎您对此功能的反馈。请在 此预览功能的 GitHub 讨论中分享您的想法和建议。
当前行为
Prisma Client 区分 null
和 undefined
null
是一个值undefined
意味着不执行任何操作
以下数据表示一个 User
表。此数据集将用于以下所有示例
id | 姓名 | 电子邮件 |
---|---|---|
1 | Nikolas | nikolas@gmail.com |
2 | Martin | martin@gmail.com |
3 | 空 | sabin@gmail.com |
4 | Tyler | tyler@gmail.com |
影响多条记录的查询中的 null
和 undefined
本节将介绍 undefined
和 null
值如何影响与数据库中多条记录交互或创建多条记录的查询行为。
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
函数,但相同的概念适用于任何可以影响多条记录的函数,例如 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": "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 解析器中的 null
和 undefined
对于此示例,请考虑一个基于以下 Prisma schema 的数据库
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: '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' }