跳到主要內容

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 檢查

當此功能啟用時

  1. 在查詢中將欄位顯式設定為 undefined 將導致執行時錯誤。
  2. 要在查詢中跳過某個欄位,請使用新的 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 區分 nullundefined

  • null 是一個
  • undefined 意味著不執行任何操作

以下資料表示一個 User 表。此資料集將用於以下所有示例

id姓名電子郵件
1Nikolasnikolas@gmail.com
2Martinmartin@gmail.com
3sabin@gmail.com
4Tylertyler@gmail.com

影響多條記錄的查詢中的 nullundefined

本節將介紹 undefinednull 值如何影響與資料庫中多條記錄互動或建立多條記錄的查詢行為。

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 函式,但相同的概念適用於任何可以影響多條記錄的函式,例如 updateManydeleteMany

影響單條記錄的查詢中的 nullundefined

本節將介紹 undefinednull 值如何影響與資料庫中單條記錄互動或建立單條記錄的查詢行為。

警告

注意nullfindUnique() 查詢中不是有效的過濾值。

在影響單條記錄的查詢的過濾條件中使用 nullundefined 時的查詢行為,與上一節中描述的行為非常相似。

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 解析器中的 nullundefined

對於此示例,請考慮一個基於以下 Prisma schema 的資料庫

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}

在以下更新使用者的 GraphQL 變更中,authorEmailname 都接受 null。從 GraphQL 的角度來看,這意味著欄位是可選的

type Mutation {
// Update author's email or name, or both - or neither!
updateUser(id: Int!, authorEmail: String, authorName: String): User!
}

但是,如果您將 authorEmailauthorNamenull 值傳遞給 Prisma Client,將會發生以下情況

  • 如果 args.authorEmailnull,查詢將失敗email 不接受 null
  • 如果 args.authorNamenull,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,請將 emailname 的值設定為 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
},
})
},

nullundefined 對條件語句的影響

使用條件過濾時存在一些注意事項,這可能會產生意外結果。鑑於 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。當執行此查詢時,不返回任何資料。

這與 ANDNOT 運算子形成對比,如果您傳入 undefined 值,它們都將返回所有使用者。

這是因為將 undefined 值傳遞給 ANDNOT 運算子與根本不傳遞任何值相同,這意味著示例中的 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' }
© . This site is unofficial and not affiliated with Prisma Data, Inc.