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' }