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