メインコンテンツにスキップ

参照整合性アクション

参照整合性アクションは、アプリケーションが関連レコードを削除または更新したときにレコードに何が起こるかを決定します。

バージョン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レコードも削除されることを意味します。

schema.prisma
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はデフォルトを使用します

危険

バージョン2.26.0より前のバージョンからアップグレードする場合:参照整合性アクションのアップグレードパスのセクションを確認することが非常に重要です。Prisma ORMの参照整合性アクションのサポートは、ランタイム時にカスケード削除を防ぐPrisma Clientの安全ネットを削除しますデータベースをアップグレードせずにこの機能を使用すると、古いデフォルトアクション - ON DELETE CASCADE - がアクティブになります。これにより、予期しないカスケード削除が発生する可能性があります。

参照整合性アクションとは?

参照整合性アクションは、updateまたはdeleteクエリを実行したときに、参照レコードがデータベースによってどのように処理されるかを定義するポリシーです。

データベースレベルでの参照整合性アクション

参照整合性アクションは、データベースの参照整合性を維持するために存在する外部キー制約の機能です。

Prismaスキーマでデータモデル間のリレーションシップを定義する場合、リレーションフィールドデータベースには存在しません)と、スカラーフィールドデータベースに存在します)を使用します。これらの外部キーは、データベースレベルでモデルを接続します。

参照整合性とは、これらの外部キーが、関連するデータベーステーブルの既存の主キー値を参照する必要があることを意味します。Prismaスキーマでは、これは通常、関連モデルのidフィールドによって表されます。

デフォルトでは、データベースは参照整合性に違反する操作(たとえば、参照レコードの削除など)を拒否します。

参照整合性アクションの使用方法

参照整合性アクションは、@relation属性で定義され、基盤となるデータベースの外部キー制約のアクションにマッピングされます。参照整合性アクションを指定しない場合、Prisma ORMはデフォルトにフォールバックします

次のモデルは、UserPostの間の1対多リレーションと、PostTagの間の多対多リレーションを、明示的に定義された参照整合性アクションとともに定義します。

schema.prisma
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を削除すると、Cascade参照整合性アクションを使用して、対応するタグ割り当てもTagOnPostsで削除されます
  • Userを削除すると、SetNull参照整合性アクションのため、フィールド値をNullに設定することで、すべての投稿から作成者が削除されます。これを許可するには、PostUseruserIdがオプションフィールドである必要があります。

Prisma ORMは、次の参照整合性アクションをサポートしています

参照整合性アクションのデフォルト

参照整合性アクションを指定しない場合、Prisma ORMは次のデフォルトを使用します

オプションリレーション必須リレーション
onDeleteSetNull制限
onUpdateカスケードカスケード

たとえば、次のスキーマでは、すべての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にはRestrictonUpdateにはCascadeのデフォルトの参照整合性アクションが適用されることを意味します。

注意点

次の注意点が適用されます

  • 参照整合性アクションは、暗黙的な多対多リレーションではサポートされていません。参照整合性アクションを使用するには、明示的な多対多リレーションを定義し、結合テーブルで参照整合性アクションを定義する必要があります。
  • 参照整合性アクションと必須/オプションリレーションの特定の組み合わせは互換性がありません。たとえば、必須リレーションでSetNullを使用すると、参照レコードを削除するときに、NULL非許容制約に違反するため、データベースエラーが発生します。詳細については、このGitHub issueを参照してください。

参照整合性アクションの種類

次の表は、各データベースがサポートする参照整合性アクションを示しています。

データベースカスケード制限NoActionSetNullSetDefault
PostgreSQL✔️✔️✔️✔️⌘✔️
MySQL/MariaDB✔️✔️✔️✔️❌ (✔️†)
SQLite✔️✔️✔️✔️✔️
SQL Server✔️❌‡✔️✔️✔️
CockroachDB✔️✔️✔️✔️✔️
MongoDB††✔️✔️✔️✔️

参照整合性アクションの特別なケース

参照整合性アクションは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 依存レコードの参照スカラーフィールドが更新された場合、リレーションスカラーフィールドを更新します。

使用例

schema.prisma
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 参照レコードの識別子が変更されるのを防ぎます。

使用例

schema.prisma
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削除できませんUserid変更できません

警告

RestrictアクションはMicrosoft SQL Serverでは利用できず、スキーマ検証エラーをトリガーします。代わりに、同じ結果を生成し、SQL Serverと互換性のあるNoActionを使用できます。

NoAction

NoActionアクションはRestrictに似ていますが、2つの違いは使用されているデータベースによって異なります

  • PostgreSQL: NoActionは、チェック(テーブルに参照行が存在するかどうか)をトランザクションの後半まで延期することを許可します。詳細については、PostgreSQLドキュメントを参照してください。
  • MySQL: NoActionRestrictとまったく同じように動作します。詳細については、MySQLドキュメントを参照してください。
  • SQLite: 関連する主キーが変更または削除された場合、アクションは実行されません。詳細については、SQLiteドキュメントを参照してください。
  • SQL Server: 参照レコードが削除または変更されると、エラーが発生します。詳細については、SQL Serverドキュメントを参照してください。
  • MongoDB (バージョン3.6.0以降でプレビュー): レコードが変更または削除された場合、関連するレコードには何も行われません。
警告

データベースで外部キーを使用するのではなく、Prisma Clientでリレーションを管理する場合、現在Prisma ORMは参照整合性アクションのみを実装していることに注意する必要があります。外部キーは制約も作成し、これらの制約に違反する方法でデータを操作することを不可能にします。クエリを実行する代わりに、データベースはエラーで応答します。Prisma Clientで参照整合性をエミュレートする場合、これらの制約は作成されないため、参照整合性アクションをNoActionに設定すると、参照整合性を破るのを防ぐためのチェックは行われません。

使用例

schema.prisma
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削除できませんUserid変更できません

SetNull

  • onDelete: SetNull 参照オブジェクトのスカラーフィールドはNULLに設定されます。

  • onUpdate: SetNull 参照オブジェクトの識別子を更新すると、参照オブジェクトのスカラーフィールドはNULLに設定されます。

SetNullはオプションリレーションでのみ機能します。必須リレーションでは、スカラーフィールドをNULLにできないため、ランタイムエラーがスローされます。

schema.prisma
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に設定されます。

Useridを変更すると、authorIdは作成したすべての投稿でNULLに設定されます。

SetDefault

  • onDelete: SetDefault 参照オブジェクトのスカラーフィールドは、フィールドのデフォルト値に設定されます。

  • onUpdate: SetDefault 参照オブジェクトのスカラーフィールドは、フィールドのデフォルト値に設定されます。

これらは、@defaultを使用してリレーションスカラーフィールドにデフォルトを設定する必要があります。いずれのスカラーフィールドにもデフォルトが指定されていない場合、ランタイムエラーがスローされます。

schema.prisma
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」に設定されます。

Userusernameが変更されると、既存の投稿の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は新しいデフォルトを使用します。

参照整合性アクションは、ケースバイケースで追加できることに注意してください。これは、1つのリレーションに追加し、残りを手動で何も指定せずにデフォルトに設定したままにできることを意味します。

エラーの確認

バージョン2.26.0にアップグレードし、参照整合性アクションのプレビュー機能を有効にする、Prisma ORMは、参照整合性を維持するためにdelete()またはdeleteMany()を使用している間、レコードの削除を防いでいました。カスタムランタイムエラーがエラーコードP2014でPrisma Clientによってスローされます。

アップグレードし、参照整合性アクションのプレビュー機能を有効にした、Prisma ORMはランタイムチェックを実行しなくなりました。代わりに、リレーション間の参照整合性を維持するためにカスタム参照整合性アクションを指定できます。

レコードの削除を防ぐためにNoActionまたはRestrictを使用する場合、エラーメッセージは2.26.0以前と比較して2.26.0以降では異なります。これは、それらがデータベースによってトリガーされるようになり、Prisma Clientではないためです。予期される新しいエラーコードはP2003です。

これらの新しいエラーを確実にキャッチするために、コードを適宜調整できます。

エラーをキャッチする例

次の例では、PostUserの間の1対多リレーションシップを持つ以下のブログスキーマを使用し、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()

コードで正しいエラーをチェックしていることを確認するために、チェックを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()