`null` 和 `undefined`
在 Prisma ORM 中,如果将 `undefined` 作为值传递,它将不会包含在生成的查询中。这种行为可能导致意外结果和数据丢失。为了防止这种情况,我们强烈建议更新到 5.20.0 或更高版本,以利用下面描述的新 `strictUndefinedChecks` 预览功能。
有关当前行为(不含 `strictUndefinedChecks` 预览功能)的文档,请参阅当前行为。
`strictUndefinedChecks`(严格的未定义检查,预览功能)
Prisma ORM 5.20.0 引入了一个名为 `strictUndefinedChecks` 的新预览功能。此功能改变了 Prisma Client 处理 `undefined` 值的方式,提供更好的保护,避免意外数据丢失或不期望的查询行为。
启用 `strictUndefinedChecks`
要启用此功能,请将以下内容添加到您的 Prisma schema 中
generator client {
provider = "prisma-client-js"
previewFeatures = ["strictUndefinedChecks"]
}
使用 `strictUndefinedChecks`
启用此功能后
- 在查询中明确将字段设置为 `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` 表示**什么都不做**
这在**具有 GraphQL 上下文的 Prisma ORM** 中尤其重要,其中 `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 mutation 中,`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` 值,它们都会返回所有用户。
这是因为向 `AND` 或 `NOT` 操作符传递 `undefined` 值等同于完全不传递任何内容,这意味着示例中的 `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' }