參照操作升級路徑
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 版本時寫入資料庫外部索引鍵的預設參照操作
| 子句 | 可選關係 | 強制關係 |
|---|---|---|
onDelete | SetNull | Cascade |
onUpdate | Cascade | Cascade |
除了資料庫參照操作之外,Prisma Client 2.x 版本還強制執行以下操作
| 子句 | 可選關係 | 強制關係 |
|---|---|---|
onDelete | SetNull | Restrict |
onUpdate | Cascade | Cascade |
升級路徑
升級時有幾種路徑可以選擇,根據期望的結果會產生不同的效果。
如果你當前使用遷移工作流程,可以執行 prisma db pull 來檢查預設值如何反映在你的 schema 中。如果需要,你可以手動更新資料庫。
你也可以選擇跳過檢查預設值,並執行遷移來使用新的預設值更新資料庫。
使用內省
如果你內省你的資料庫,資料庫級別配置的參照操作將反映在你的 Prisma Schema 中。如果你一直使用 Prisma Migrate 或 prisma db push 來管理資料庫 schema,這些很可能是 <=2.25.0 的預設值。
當你執行內省時,Prisma ORM 會比較資料庫中所有的外部索引鍵與 schema,如果 SQL 語句 ON DELETE 和 ON UPDATE 與預設值**不**匹配,它們將被明確設定在 schema 檔案中。
內省後,你可以檢查 schema 中的非預設子句。最重要的子句是 onDelete,在 2.25.0 及更早版本中,它預設為 Cascade。
如果你正在使用 delete() 或 deleteAll() 方法,**現在將執行級聯刪除,因為 Prisma Client 中之前在執行時阻止級聯刪除的安全網已被移除**。請務必檢查你的程式碼並進行相應的調整。
確保你對 schema 中所有 onDelete: Cascade 的情況都滿意。如果不是,則:
- 修改你的 Prisma schema 並執行
db push或dev 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 版本(或啟用了 referentialActions 功能標誌的 2.26.0 及更高版本)**之前**,Prisma ORM 在使用 delete() 或 deleteMany() 時會阻止刪除記錄以保持參照完整性。Prisma Client 會丟擲一個帶有錯誤程式碼 P2014 的自定義執行時錯誤。
升級**之後**,Prisma ORM 不再執行執行時檢查。你可以改為指定自定義參照操作以保持關係之間的參照完整性。
當你使用 NoAction 或 Restrict 來阻止記錄刪除時,3.0.1 及更高版本(或啟用了 referentialActions 功能標誌的 2.26.0 版本)中的錯誤訊息將與之前的版本不同。這是因為它們現在由資料庫**而非** Prisma Client 觸發。可以預期的新錯誤程式碼是 P2003,因此你應該檢查你的程式碼並進行相應調整。
錯誤捕獲示例
以下示例使用下面的部落格 schema,其中 Post 和 User 之間存在一對多關係,並在 author 欄位上設定了 Restrict 參照操作。
這意味著如果一個使用者有帖子,那麼該使用者(及其帖子)**不能**被刪除。
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)
})