リレーションモード
Prisma スキーマでは、レコード間のリレーションは @relation
属性で定義されます。たとえば、次のスキーマでは、User
モデルと Post
モデルの間に 1 対多リレーションがあります。
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 クエリでのみ利用可能であり、raw クエリには適用されません。
どの外部キー制約がエミュレートされますか?
レコードを更新すると、Prisma ORM は外部キー制約をエミュレートします。これは、別のレコードとのリレーションを持つレコードを更新する場合、関連レコードが存在する必要があることを意味します。レコードが存在しない場合、Prisma Client はエラーメッセージを返します。
ただし、レコードを作成する場合、Prisma ORM は外部キー制約をエミュレートしません。無効なデータを作成することができます。
どの参照アクションがエミュレートされますか?
関連レコードを持つレコードを更新または削除すると、Prisma ORM は参照アクションをエミュレートします。
次の表は、各データベースコネクタで利用可能なエミュレートされた参照アクションを示しています。
データベース | Cascade | Restrict | 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 ORM は、prisma format
または prisma validate
を実行すると、次の警告を表示します。
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
の値を追加するか、relationMode
フィールドが既に存在する場合は値を 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 は次のマイグレーションですべてのリレーションの外部キーを作成します。