參考動作
參考動作決定當您的應用程式刪除或更新相關記錄時,記錄會發生什麼情況。
從 2.26.0 版本開始,您可以在 Prisma 結構描述的關聯欄位上定義參考動作。這允許您在 Prisma ORM 層級定義串聯刪除和串聯更新等參考動作。
版本差異
- 如果您使用 3.0.1 或更新版本,您可以依照本頁面的說明使用參考動作。
- 如果您使用 2.26.0 到 3.0.0 之間的版本,您可以依照本頁面的說明使用參考動作,但您必須啟用預覽功能旗標
referentialActions
。 - 如果您使用 2.25.0 或更早版本,您可以在資料庫中手動設定串聯刪除。
在以下範例中,在 Post
模型上的 author
欄位新增 onDelete: Cascade
,表示刪除 User
記錄也會刪除所有相關的 Post
記錄。
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 ORM 會使用預設值。
什麼是參考動作?
參考動作是定義當您執行 update
或 delete
查詢時,資料庫如何處理參考記錄的策略。
資料庫層級的參考動作
如何使用參考動作
參考動作是在@relation
屬性中定義,並映射到基礎資料庫中外鍵約束上的動作。如果您未指定參考動作,Prisma ORM 會回退到預設值。
以下模型定義了 User
和 Post
之間的一對多關聯,以及 Post
和 Tag
之間的多對多關聯,並明確定義了參考動作
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
tags TagOnPosts[]
User User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade)
userId Int?
}
model TagOnPosts {
id Int @id @default(autoincrement())
post Post? @relation(fields: [postId], references: [id], onUpdate: Cascade, onDelete: Cascade)
tag Tag? @relation(fields: [tagId], references: [id], onUpdate: Cascade, onDelete: Cascade)
postId Int?
tagId Int?
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts TagOnPosts[]
}
此模型明確定義了以下參考動作
- 如果您刪除
Tag
,則對應的標籤指派也會在TagOnPosts
中刪除,使用Cascade
參考動作 - 如果您刪除
User
,則會從所有貼文中移除作者,方法是將欄位值設定為Null
,因為使用了SetNull
參考動作。為了允許這樣做,User
和userId
必須是Post
中的選用欄位。
Prisma ORM 支援以下參考動作
參考動作預設值
如果您未指定參考動作,Prisma ORM 會使用以下預設值
子句 | 選用關聯 | 強制關聯 |
---|---|---|
onDelete | SetNull | Restrict |
onUpdate | Cascade | Cascade |
例如,在以下結構描述中,所有 Post
記錄都必須透過 author
關聯連接到 User
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
結構描述未在強制 author
關聯欄位上明確定義參考動作,這表示 onDelete
的預設參考動作 Restrict
和 onUpdate
的 Cascade
會套用。
注意事項
以下注意事項適用
- 參考動作不支援隱含多對多關聯。若要使用參考動作,您必須定義明確的多對多關聯,並在聯結表格上定義您的參考動作。
- 參考動作與必要/選用關聯的某些組合不相容。例如,在必要關聯上使用
SetNull
會在刪除參考記錄時導致資料庫錯誤,因為非可為 Null 的約束會被違反。請參閱此 GitHub 問題以取得更多資訊。
參考動作的類型
下表顯示每個資料庫支援哪些參考動作。
資料庫 | Cascade | Restrict | NoAction | SetNull | SetDefault |
---|---|---|---|---|---|
PostgreSQL | ✔️ | ✔️ | ✔️ | ✔️⌘ | ✔️ |
MySQL/MariaDB | ✔️ | ✔️ | ✔️ | ✔️ | ❌ (✔️†) |
SQLite | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
SQL Server | ✔️ | ❌‡ | ✔️ | ✔️ | ✔️ |
CockroachDB | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
MongoDB†† | ✔️ | ✔️ | ✔️ | ✔️ | ❌ |
- † 請參閱MySQL 的特殊情況。
- ⌘ 請參閱PostgreSQL 的特殊情況。
- ‡ 請參閱SQL Server 的特殊情況。
- †† MongoDB 的參考動作在 Prisma ORM 3.7.0 和更新版本中可用。
參考動作的特殊情況
參考動作是 ANSI SQL 標準的一部分。但是,在某些特殊情況下,某些關聯式資料庫會偏離標準。
MySQL/MariaDB
MySQL/MariaDB 和底層的 InnoDB 儲存引擎不支援 SetDefault
。確切行為取決於資料庫版本
- 在 MySQL 8 和更新版本以及 MariaDB 10.5 和更新版本中,
SetDefault
實際上充當NoAction
的別名。您可以使用SET DEFAULT
參考動作定義表格,但在執行階段會觸發外鍵約束錯誤。 - 在 MySQL 5.6 和更新版本以及 MariaDB 10.5 之前的版本中,嘗試使用
SET DEFAULT
參考動作建立表格定義會因語法錯誤而失敗。
因此,當您將 mysql
設定為資料庫供應商時,Prisma ORM 會警告使用者將 Prisma 結構描述中的 SetDefault
參考動作替換為另一個動作。
PostgreSQL
PostgreSQL 是 Prisma ORM 支援的唯一資料庫,允許您定義參考非可為 Null 欄位的 SetNull
參考動作。但是,當動作在執行階段觸發時,這會引發外鍵約束錯誤。
因此,當您在(預設)foreignKeys
關聯模式中將 postgres
設定為資料庫供應商時,Prisma ORM 會警告使用者將任何包含在具有 SetNull
參考動作的 @relation
屬性中的欄位標記為選用。對於所有其他資料庫供應商,Prisma ORM 會因驗證錯誤而拒絕結構描述。
SQL Server
Restrict
不適用於 SQL Server 資料庫,但您可以使用 NoAction
來代替。
Cascade
onDelete: Cascade
刪除參考記錄將會觸發參考記錄的刪除。onUpdate: Cascade
如果相依記錄的參考純量欄位已更新,則更新關聯純量欄位。
範例用法
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade, onUpdate: Cascade)
authorId Int
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
使用 Cascade
的結果
如果刪除 User
記錄,則也會刪除其貼文。如果更新使用者的 id
,則也會更新對應的 authorId
。
如何使用串聯刪除
Restrict
onDelete: Restrict
如果存在任何參考記錄,則防止刪除。onUpdate: Restrict
防止變更參考記錄的識別碼。
範例用法
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Restrict)
authorId Int
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
使用 Restrict
的結果
有貼文的 User
無法刪除。User
的 id
無法變更。
Restrict
動作在 Microsoft SQL Server 上不可用,並且會觸發結構描述驗證錯誤。相反地,您可以使用 NoAction
,這會產生相同的結果,並且與 SQL Server 相容。
NoAction
NoAction
動作與 Restrict
類似,兩者之間的差異取決於使用的資料庫
- PostgreSQL:
NoAction
允許將檢查(表格上是否存在參考列)延遲到交易的稍後時間。請參閱 PostgreSQL 文件以取得更多資訊。 - MySQL:
NoAction
的行為與Restrict
完全相同。請參閱 MySQL 文件以取得更多資訊。 - SQLite:當相關的主鍵被修改或刪除時,不採取任何動作。請參閱 SQLite 文件以取得更多資訊。
- SQL Server:當參考記錄被刪除或修改時,會引發錯誤。請參閱 SQL Server 文件以取得更多資訊。
- MongoDB(從 3.6.0 版本開始預覽):當記錄被修改或刪除時,不會對任何相關記錄執行任何操作。
如果您是在 Prisma Client 中管理關聯,而不是在資料庫中使用外鍵,您應該注意,目前 Prisma ORM 僅實作參考動作。外鍵也會建立約束,這使得以會違反這些約束的方式操作資料成為不可能:資料庫會回應錯誤,而不是執行查詢。如果您在 Prisma Client 中模擬參考完整性,則不會建立這些約束,因此如果您將參考動作設定為 NoAction
,則不會有檢查來防止您破壞參考完整性。
範例用法
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: NoAction, onUpdate: NoAction)
authorId Int
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
使用 NoAction
的結果
有貼文的 User
無法刪除。User
的 id
無法變更。
SetNull
-
onDelete: SetNull
參考物件的純量欄位將會設定為NULL
。 -
onUpdate: SetNull
當更新參考物件的識別碼時,參考物件的純量欄位將會設定為NULL
。
SetNull
僅適用於選用關聯。在必要關聯上,將會擲回執行階段錯誤,因為純量欄位不能為 Null。
model Post {
id Int @id @default(autoincrement())
title String
author User? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: SetNull)
authorId Int?
}
model User {
id Int @id @default(autoincrement())
posts Post[]
}
使用 SetNull
的結果
當刪除 User
時,所有其撰寫的貼文的 authorId
都會設定為 NULL
。
當變更 User
的 id
時,所有其撰寫的貼文的 authorId
都會設定為 NULL
。
SetDefault
-
onDelete: SetDefault
參考物件的純量欄位將會設定為欄位的預設值。 -
onUpdate: SetDefault
參考物件的純量欄位將會設定為欄位的預設值。
這些需要使用 @default
為關聯純量欄位設定預設值。如果未為任何純量欄位提供預設值,則會擲回執行階段錯誤。
model Post {
id Int @id @default(autoincrement())
title String
authorUsername String? @default("anonymous")
author User? @relation(fields: [authorUsername], references: [username], onDelete: SetDefault, onUpdate: SetDefault)
}
model User {
username String @id
posts Post[]
}
使用 SetDefault
的結果
當刪除 User
時,其現有貼文的 authorUsername
欄位值將會設定為 'anonymous'。
當 User
的 username
變更時,其現有貼文的 authorUsername
欄位值將會設定為 'anonymous'。
資料庫特定需求
如果您在資料模型中有自我關聯或循環關聯,則 MongoDB 和 SQL Server 對於參考動作有特定需求。如果您有具有多個串聯路徑的關聯,SQL Server 也有特定需求。
從 2.25.0 和更早版本的升級路徑
升級時您可以採取幾種路徑,這些路徑會根據所需的結果產生不同的結果。
如果您目前使用移轉工作流程,您可以執行內省以檢查預設值如何在您的結構描述中反映。然後,您可以手動更新資料庫(如果需要)。
您也可以決定略過檢查預設值,並執行移轉以使用新的預設值更新您的資料庫。
以下假設您已升級到 2.26.0 或更新版本,並啟用預覽功能旗標,或升級到 3.0.0 或更新版本
使用內省
如果您內省您的資料庫,則在資料庫層級設定的參考動作將會在您的 Prisma 結構描述中反映。如果您一直使用 Prisma Migrate 或 prisma db push
來管理資料庫結構描述,這些很可能是 2.25.0 和更早版本的預設值。
當您執行內省時,Prisma ORM 會將資料庫中的所有外鍵與結構描述進行比較,如果 SQL 陳述式 ON DELETE
和 ON UPDATE
與預設值不符,則它們將會在結構描述檔案中明確設定。
在內省之後,您可以檢閱結構描述中的非預設子句。要檢閱的最重要子句是 onDelete
,在 2.25.0 和更早版本中,預設值為 Cascade
。
如果您使用 delete()
或 deleteMany()
方法,串聯刪除現在將會執行,因為 referentialActions
預覽功能移除了 Prisma Client 中先前防止在執行階段串聯刪除的安全網。請務必檢查您的程式碼並據此進行任何調整。
請確保您對結構描述中每個 onDelete: Cascade
的情況感到滿意。如果不是,請執行以下操作:
- 修改您的 Prisma 結構描述,並
db push
或dev migrate
以變更資料庫
或
- 如果您使用僅限內省的工作流程,請手動更新底層資料庫
以下範例會導致串聯刪除,如果刪除 User
,則其所有 Post
也會被刪除。
部落格結構描述範例
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 VSCode 擴充功能自動新增到您的 prisma 結構描述中。如果您希望使用新的預設值以外的任何值,則必須手動新增它們。
在您的 Prisma 結構描述中明確定義參考動作是選用的。如果您未明確定義關聯的參考動作,Prisma ORM 會使用新的預設值。
請注意,參考動作可以逐個新增。這表示您可以將它們新增到單一關聯,並將其餘關聯設定為預設值,而無需手動指定任何內容。
檢查錯誤
在升級到 2.26.0 並啟用參考動作預覽功能之前,Prisma ORM 會在使用 delete()
或 deleteMany()
時防止刪除記錄,以保持參考完整性。Prisma Client 會擲回具有錯誤代碼 P2014
的自訂執行階段錯誤。
在升級並啟用參考動作預覽功能之後,Prisma ORM 不再執行執行階段檢查。您可以改為指定自訂參考動作,以保持關聯之間的參考完整性。
當您使用 NoAction
或 Restrict
來防止刪除記錄時,2.26.0 之後的錯誤訊息會與 2.26.0 之前的錯誤訊息不同。這是因為它們現在是由資料庫觸發,而不是由 Prisma Client 觸發。可以預期的新錯誤代碼是 P2003
。
為了確保您捕捉到這些新錯誤,您可以據此調整您的程式碼。
捕捉錯誤的範例
以下範例使用以下部落格結構描述,其中 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()
為了確保您在程式碼中檢查正確的錯誤,請修改您的檢查以尋找 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()