跳到主要內容

參考動作升級路徑

Prisma ORM 2.x 版本在某些 Prisma Client 函式中會阻止刪除關聯的記錄,且不允許您在 Prisma Schema 中設定參考動作來變更此行為。

Prisma ORM 3.x 及更高版本讓您可以透過在模型的關聯上明確設定參考動作,來控制刪除或更新記錄時應發生的情況。升級後,Prisma Client 將不再強制執行任何參考動作,而寫入資料庫外鍵的任何動作將定義刪除或更新記錄時的行為。

Prisma Migrate 3.x 在將外鍵約束寫入資料庫時,將使用先前由 Prisma Client 執行的動作作為新的預設值。

Prisma ORM 2.x 行為

當在必要關聯上使用 Prisma Client 調用 delete()deleteAll() 方法時,會執行運行時檢查,如果記錄正在參考相關物件,則會阻止刪除記錄。無論外鍵如何定義,這都會阻止級聯行為

在未升級的情況下,Prisma ORM 2 中的行為完全不允許設定參考動作。 請參閱 Prisma ORM 2.x 預設參考動作

如果您實際上需要使用資料庫中配置的級聯行為,您可以使用 raw SQL 查詢來 刪除多個參考記錄。這是因為 Prisma Client 將不會對原始查詢執行運行時檢查。

Prisma ORM 2.x 預設參考動作

以下是使用 Prisma Migrate 2.x 版本時,寫入資料庫外鍵的預設參考動作

子句可選關聯強制關聯
onDeleteSetNullCascade
onUpdateCascadeCascade

除了資料庫參考動作之外,Prisma Client 2.x 版本還強制執行以下動作

子句可選關聯強制關聯
onDeleteSetNullRestrict
onUpdateCascadeCascade

升級路徑

升級時您可以採取幾種路徑,這些路徑會根據所需的結果產生不同的結果。

如果您目前使用遷移工作流程,您可以運行 prisma db pull 來檢查預設值如何在您的 schema 中反映。然後,您可以根據需要手動更新您的資料庫。

您也可以決定跳過檢查預設值,並運行遷移以使用新的預設值更新您的資料庫。

使用內省

如果您 內省您的資料庫,則在資料庫層級配置的參考動作將反映在您的 Prisma Schema 中。如果您一直使用 Prisma Migrate 或 prisma db push 來管理資料庫 schema,則這些很可能是 <=2.25.0 的預設值。

當您運行內省時,Prisma ORM 會將資料庫中的所有外鍵與 schema 進行比較,如果 SQL 陳述式 ON DELETEON UPDATE 符合預設值,它們將在 schema 檔案中明確設定。

內省後,您可以檢閱 schema 中非預設的子句。最重要的檢閱子句是 onDelete,在 2.25.0 及更早版本中,預設為 Cascade

危險

如果您正在使用 delete()deleteAll() 方法,現在將執行級聯刪除,因為 Prisma Client 中先前阻止運行時級聯刪除的安全網已被移除。請務必檢查您的程式碼並進行相應的調整。

請確保您對 schema 中每個 onDelete: Cascade 的情況感到滿意。如果不是,請執行以下操作之一

  • 修改您的 Prisma schema 並 db pushdev migrate 以變更資料庫
  • 如果您在工作流程中僅使用 prisma db pull,則手動更新底層資料庫

以下範例將導致級聯刪除,這表示如果 User 被刪除,則他們的所有 Post 也將被刪除。

部落格 schema 範例

model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
}

model User {
id Int @id @default(autoincrement())
posts Post[]
}

使用遷移

當運行 遷移(或 prisma db push 命令)時,新的預設值將應用於您的資料庫。

資訊

與您第一次運行 prisma db pull 時不同,新的參考動作子句和屬性將不會由 Prisma VSCode 擴充功能自動新增到您的 Prisma schema 中。如果您希望使用新的預設值以外的任何值,則必須手動新增它們。

在您的 Prisma schema 中明確定義參考動作是可選的。如果您沒有為關聯明確定義參考動作,Prisma ORM 將使用新的預設值

請注意,可以根據具體情況新增參考動作。這表示您可以將它們新增到單一關聯,並將其餘關聯設定為預設值,而無需手動指定任何內容。

檢查錯誤

在升級到 3.0.1 版本(或 2.26.0 及更高版本且啟用 referentialActions 功能標誌的版本)之前,Prisma ORM 會在使用 delete()deleteMany() 時阻止刪除記錄,以保留參考完整性。Prisma Client 會拋出帶有錯誤代碼 P2014 的自訂運行時錯誤。

升級之後,Prisma ORM 不再執行運行時檢查。您可以改為指定自訂參考動作,以保留關聯之間的參考完整性。

當您使用 NoActionRestrict 來阻止刪除記錄時,在 3.0.1 及更高版本(或 2.26.0 且啟用 referentialActions 功能標誌的版本)中,錯誤訊息將與之前的版本不同。這是因為它們現在由資料庫觸發,而不是 Prisma Client。預期的新錯誤代碼是 P2003,因此您應該檢查您的程式碼以進行相應的調整。

捕捉錯誤範例

以下範例使用下面的部落格 schema,其中 PostUser 之間存在一對多關聯,並在 author 欄位上設定了 Restrict 參考動作。

這表示如果使用者有貼文,則該使用者(及其貼文)無法被刪除。

schema.prisma
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
authorId String
}

model User {
id Int @id @default(autoincrement())
posts Post[]
}

在升級之前,當您嘗試刪除有貼文的使用者時,您會收到的錯誤代碼是 P2014,其訊息為

"您嘗試進行的變更將違反 '{model_a_name}' 和 '{model_b_name}' 模型之間必要的關聯 '{relation_name}'。"

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id',
},
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
console.log(error.message)
}
}
}
}

main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})

為了確保您在程式碼中檢查正確的錯誤,請修改您的檢查以尋找 P2003,這將傳遞訊息

"外鍵約束在欄位:{field_name} 上失敗"

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
try {
await prisma.user.delete({
where: {
id: 'some-long-id'
}
})
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2014') {
if (error.code === 'P2003') {
console.log(error.message)
}
}
}
}

main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})