関連モード
Prismaスキーマでは、レコード間の関連は@relation
属性で定義されます。たとえば、次のスキーマでは、User
モデルとPost
モデルの間に一対多関連があります。
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[]
}
Prisma ORMには、レコード間の関連をどのように強制するかを指定する2つの関連モード、foreignKeys
とprisma
があります。
Prisma ORMをリレーショナルデータベースで使用する場合、デフォルトではPrisma ORMはforeignKeys
関連モードを使用します。これは、外部キーを使用してデータベースレベルでレコード間の関連を強制します。外部キーは、あるテーブルの主キーに基づいて値を取る、別のテーブルの列または列のグループです。外部キーを使用すると、以下のことが可能になります。
- 参照を壊す変更を防止する制約を設定する
- レコードへの変更がどのように処理されるかを定義する参照アクションを設定する
これらの制約と参照アクションを組み合わせることで、データの参照整合性が保証されます。
上記のスキーマの例では、PostgreSQLコネクタを使用する場合、Prisma Migrateはデフォルトで次のSQLを生成します。
-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"authorId" INTEGER NOT NULL,
CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
//highlight-start
ALTER TABLE "Post"
ADD CONSTRAINT "Post_authorId_fkey"
FOREIGN KEY ("authorId")
REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
//highlight-end
この場合、Post
テーブルのauthorId
列にある外部キー制約は、User
テーブルのid
列を参照し、投稿には既存の作者がなければならないことを保証します。ユーザーを更新または削除した場合、ON DELETE
およびON UPDATE
参照アクションはCASCADE
オプションを指定し、これによりそのユーザーに属するすべての投稿も削除または更新されます。
MongoDBやPlanetScaleなど、一部のデータベースは外部キーをサポートしていません。さらに、場合によっては、開発者が通常は外部キーをサポートしているリレーショナルデータベースで外部キーを使用しないことを好むことがあります。このような状況のために、Prisma ORMはprisma
関連モードを提供しています。これは、リレーショナルデータベースにおける関連の一部のプロパティをエミュレートします。prisma
関連モードが有効な状態でPrisma Clientを使用すると、クエリの動作は同一または類似していますが、参照アクションと一部の制約はデータベースではなくPrismaエンジンによって処理されます。
Prisma Clientにおける参照整合性と参照アクションのエミュレーションには、パフォーマンス上の影響があります。基盤となるデータベースが外部キーをサポートしている場合は、通常、そちらの方が推奨される選択肢です。
Prismaスキーマで関連モードを設定する方法
関連モードを設定するには、datasource
ブロックにrelationMode
フィールドを追加します。
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
関連モードを設定する機能は、Prisma ORMバージョン3.1.1のreferentialIntegrity
プレビュー機能の一部として導入され、Prisma ORMバージョン4.8.0以降で一般提供されています。relationMode
フィールドはPrisma ORMバージョン4.5.0で名前が変更され、以前はreferentialIntegrity
という名前でした。
リレーショナルデータベースの場合、利用可能なオプションは次のとおりです。
foreignKeys
: これは、外部キーを使用してデータベース内の関連を処理します。これはすべてのリレーショナルデータベースコネクタのデフォルトオプションであり、datasource
ブロックでrelationMode
が明示的に設定されていない場合にアクティブになります。prisma
: これは、Prisma Clientで関連をエミュレートします。PlanetScaleデータベースでMySQLコネクタを使用し、PlanetScaleデータベース設定でネイティブの外部キー制約を有効にしていない場合も、このオプションを有効にする必要があります。
MongoDBの場合、利用可能なオプションはprisma
関連モードのみです。このモードは、datasource
ブロックでrelationMode
が明示的に設定されていない場合にもアクティブになります。
関連モードを切り替えると、次回Prisma Migrateまたはdb push
でスキーマに変更を適用するときに、Prisma ORMはデータベースに外部キーを追加または削除します。詳細については、関連モード間の切り替えを参照してください。
foreignKeys
関連モードでリレーショナルデータベースの関連を処理する
foreignKeys
関連モードは、外部キーを使用してリレーショナルデータベースの関連を処理します。これは、リレーショナルデータベースコネクタ(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)を使用する場合のデフォルトオプションです。
foreignKeys
関連モードは、MongoDBコネクタを使用する場合は利用できません。PlanetScaleなど、一部のリレーショナルデータベースも外部キーの使用を禁止しています。これらの場合、代わりにprisma
関連モードでPrisma ORMの関連をエミュレートする必要があります。
参照整合性
foreignKeys
関連モードは、外部キー制約と参照アクションを使用して、データベースレベルで参照整合性を維持します。
外部キー制約
別のレコードとの関連を持つレコードを作成または更新する場合、関連するレコードが存在する必要があります。外部キー制約は、データベースでこの動作を強制します。レコードが存在しない場合、データベースはエラーメッセージを返します。
参照アクション
別のレコードとの関連を持つレコードを更新または削除する場合、参照アクションがデータベースでトリガーされます。関連レコードの参照整合性を維持するために、参照アクションは参照整合性を損なう変更を防ぎ、関連レコードへの変更をカスケードしたり、更新または削除されたレコードを参照するフィールドの値をnull
またはデフォルト値に設定したりします。
詳細については、参照アクションのページを参照してください。
イントロスペクション
foreignKeys
関連モードが有効な状態でdb pull
コマンドを使用してリレーショナルデータベースをイントロスペクトすると、外部キーが存在する関連に対して@relation
属性がPrismaスキーマに追加されます。
Prisma Migrateとdb push
foreignKeys
関連モードが有効な状態でPrisma Migrateまたはdb push
を使用してPrismaスキーマに変更を適用すると、スキーマ内のすべての@relation
属性に対してデータベースに外部キーが作成されます。
prisma
関連モードでPrisma ORMの関連をエミュレートする
prisma
関連モードは、各Prisma Clientクエリに対して一部の外部キー制約と参照アクションをエミュレートし、追加のデータベースクエリとロジックを使用して参照整合性を維持します。
prisma
関連モードはMongoDBコネクタのデフォルトオプションです。外部キーをサポートしないリレーショナルデータベースを使用する場合も設定する必要があります。たとえば、外部キー制約なしでPlanetScaleを使用する場合は、prisma
関連モードを使用する必要があります。
Prisma Clientでの参照整合性のエミュレーションには、参照整合性を維持するために追加のデータベースクエリを使用するため、パフォーマンス上の影響があります。基盤となるデータベースが外部キーで参照整合性を処理できる場合は、通常そちらが推奨されます。
関連のエミュレーションはPrisma Clientクエリでのみ利用可能であり、生のクエリには適用されません。
どの外部キー制約がエミュレートされますか?
レコードを更新する場合、Prisma ORMは外部キー制約をエミュレートします。これは、別のレコードとの関連を持つレコードを更新する場合、関連するレコードが存在する必要があることを意味します。レコードが存在しない場合、Prisma Clientはエラーメッセージを返します。
ただし、レコードを作成する場合、Prisma ORMは外部キー制約をエミュレートしません。無効なデータを作成できる可能性があります。
どの参照アクションがエミュレートされますか?
関連するレコードを持つレコードを更新または削除する場合、Prisma ORMは参照アクションをエミュレートします。
次の表は、各データベースコネクタで利用可能なエミュレートされた参照アクションを示しています。
データベース | カスケード | 制限 | NoAction | SetNull | SetDefault |
---|---|---|---|---|---|
PostgreSQL | ✔️ | ✔️ | ❌‡ | ✔️ | ❌† |
MySQL | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
SQLite | ✔️ | ✔️ | ❌‡ | ✔️ | ❌† |
SQL Server | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
CockroachDB | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
MongoDB | ✔️ | ✔️ | ✔️ | ✔️ | ❌† |
- †
SetDefault
参照アクションはprisma
関連モードではサポートされていません。 - ‡
NoAction
参照アクションは、PostgreSQLおよびSQLiteのprisma
関連モードではサポートされていません。代わりに、Restrict
アクションを使用してください。
エラーメッセージ
prisma
関連モードでエミュレートされた制約と参照アクションによって返されるエラーメッセージはPrisma Clientによって生成され、foreignKeys
関連モードのエラーメッセージとは若干異なります。
Example:
// foreignKeys:
... Foreign key constraint failed on the field: `ProfileOneToOne_userId_fkey (index)`
// prisma:
... The change you are trying to make would violate the required relation 'ProfileOneToOneToUserOneToOne' between the `ProfileOneToOne` and `UserOneToOne` models.
イントロスペクション
prisma
関連モードが有効な状態でdb pull
コマンドを使用してデータベースをイントロスペクトすると、関連はスキーマに自動的に追加されません。代わりに、@relation
属性を使用して関連を手動で追加する必要があります。これは一度だけ行う必要があります — 次回データベースをイントロスペクトするときは、Prisma ORMが追加された@relation
属性を保持します。
Prisma Migrateとdb push
prisma
関連モードが有効な状態でPrisma Migrateまたはdb push
を使用してPrismaスキーマに変更を適用すると、Prisma ORMはデータベースで外部キーを使用しません。
インデックス
外部キー制約を使用するリレーショナルデータベースでは、データベースは通常、外部キー列のインデックスも暗黙的に作成します。たとえば、MySQLはすべての外部キー列にインデックスを作成します。これは、外部キーチェックを高速に実行し、テーブルスキャンを必要としないようにするためです。
prisma
関連モードは外部キーを使用しないため、Prisma Migrateまたはdb push
を使用してデータベースに変更を適用してもインデックスは作成されません。代わりに、@@index
属性(または該当する場合は@unique
、@@unique
、@@id
属性)を使用して、関連のスカラーフィールドに手動でインデックスを追加する必要があります。
インデックス検証
インデックスを手動で追加しない場合、クエリはフルテーブルスキャンを必要とする可能性があります。これは低速であり、アクセスされた行ごとに課金されるデータベースプロバイダーでは高価になる可能性があります。これを避けるために、Prisma ORMは、スキーマに@relation
で使用されているがインデックスが定義されていないフィールドが含まれている場合に警告します。たとえば、User
モデルとPost
モデルの間に関連がある次のスキーマを考えてみましょう。
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
relationMode = "prisma"
}
model User {
id Int @id
posts Post[]
}
model Post {
id Int @id
userId Int
user User @relation(fields: [userId], references: [id])
}
prisma format
またはprisma validate
を実行すると、Prisma ORMは次の警告を表示します。
With `relationMode = "prisma"`, no foreign keys are used, so relation fields will not benefit from the index usually created by the relational database under the hood. This can lead to poor performance when querying these fields. We recommend adding an index manually.
これを解決するには、Post
モデルにインデックスを追加します。
model Post {
id Int @id
userId Int
user User @relation(fields: [userId], references: [id])
@@index([userId])
}
Prisma VS Code拡張機能(または他のエディタの言語サーバー)を使用すると、警告は必要なインデックスを追加するクイックフィックスで強化されます。
関連モード間の切り替え
関連モード間の切り替えは、リレーショナルデータベースコネクタ(PostgreSQL、MySQL、SQLite、SQL Server、CockroachDB)を使用している場合にのみ可能です。
foreignKeys
からprisma
への切り替え
リレーショナルデータベースを使用しており、datasource
ブロックにrelationMode
フィールドが含まれていない場合のデフォルトの関連モードはforeignKeys
です。prisma
関連モードに切り替えるには、relationMode
フィールドにprisma
の値を設定して追加するか、既存の場合はprisma
に更新します。
関連モードをforeignKeys
からprisma
に切り替えると、Prisma Migrateまたはdb push
でスキーマに変更を初めて適用した後、Prisma ORMは次のマイグレーションで以前に作成されたすべての外部キーを削除します。
同じデータベースを使い続ける場合は、通常通り作業を続行できます。外部キーをまったくサポートしないデータベースに切り替える場合、既存のマイグレーション履歴には外部キーを作成するSQL DDLが含まれており、これらのマイグレーションを再実行する必要がある場合にエラーを引き起こす可能性があります。この場合、migrations
ディレクトリを削除することをお勧めします。(外部キーをサポートしないPlanetScaleを使用する場合は、通常、Prisma Migrateではなくdb push
を使用することをお勧めします。)
prisma
からforeignKeys
への切り替え
prisma
関連モードからforeignKeys
関連モードに切り替えるには、relationMode
フィールドの値をprisma
からforeignKeys
に更新します。これを行うには、データベースが外部キーをサポートしている必要があります。関連モードを切り替えた後、初めてPrisma Migrateまたはdb push
でスキーマに変更を適用すると、Prisma ORMは次のマイグレーションですべての関連に対して外部キーを作成します。