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

参照整合性アクションのアップグレードパス

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 が raw クエリでランタイムチェックを実行しないためです。

Prisma ORM 2.x のデフォルトの参照整合性アクション

以下は、Prisma Migrate バージョン 2.x を使用する場合にデータベースの外部キーに書き込まれるデフォルトの参照整合性アクションです。

オプションのリレーション必須のリレーション
onDeleteSetNullCascade
onUpdateCascadeCascade

データベースの参照整合性アクションに加えて、以下の actions は Prisma Client バージョン 2.x で強制されます。

オプションのリレーション必須のリレーション
onDeleteSetNullRestrict
onUpdateCascadeCascade

アップグレードパス

アップグレード時に選択できるパスはいくつかあり、目的の結果に応じて異なる結果が得られます。

現在移行ワークフローを使用している場合は、prisma db pull を実行して、デフォルトがスキーマにどのように反映されているかを確認できます。必要に応じて、データベースを手動で更新できます。

デフォルトの確認をスキップして、移行を実行してデータベースを新しいデフォルト値で更新することもできます。

イントロスペクションの使用

データベースを イントロスペクト すると、データベースレベルで構成された参照整合性アクションが Prisma Schema に反映されます。Prisma Migrate または prisma db push を使用してデータベーススキーマを管理している場合、これらは <=2.25.0 デフォルト値 である可能性があります。

イントロスペクションを実行すると、Prisma ORM はデータベース内のすべての外部キーをスキーマと比較し、SQL ステートメント ON DELETE および ON UPDATE がデフォルト値と一致しない場合、スキーマファイルに明示的に設定されます。

イントロスペクト後、スキーマ内の非デフォルト句を確認できます。確認する最も重要な句は onDelete で、バージョン 2.25.0 以前ではデフォルトで Cascade になっています。

危険

delete() または deleteAll() メソッドのいずれかを使用している場合、カスケード削除が実行されるようになります。以前はランタイム時にカスケード削除を防いでいた Prisma Client の安全ネットが削除されたためです。コードを確認し、必要に応じて調整してください。

スキーマ内の onDelete: Cascade のすべてのケースに問題がないことを確認してください。そうでない場合は、次のいずれかを行います。

  • Prisma スキーマを変更して db push または dev migrate を実行してデータベースを変更する
  • ワークフローで prisma db pull のみを使用する場合は、基盤となるデータベースを手動で更新します

次の例では、カスケード削除が発生します。つまり、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 db pull を初めて実行する場合とは異なり、新しい参照整合性アクション句とプロパティは、Prisma VSCode 拡張機能によって Prisma スキーマに自動的に追加されません。新しいデフォルト以外のものを使用する場合は、手動で追加する必要があります。

Prisma スキーマで参照整合性アクションを明示的に定義することはオプションです。リレーションの参照整合性アクションを明示的に定義しない場合、Prisma ORM は 新しいデフォルト を使用します。

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

エラーの確認

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

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

NoAction または Restrict を使用してレコードの削除を防ぐ場合、エラーメッセージはバージョン 3.0.1 以降 (または referentialActions 機能フラグが有効になっている 2.26.0) では、それ以前のバージョンと比較して異なります。これは、エラーがデータベースによってトリガーされ、Prisma Client によってトリガーされないためです。予想される新しいエラーコードは P2003 なので、コードを確認してそれに応じて調整する必要があります。

エラー捕捉の例

次の例では、PostUser の間の 1 対多のリレーションシップを持つ以下のブログスキーマを使用し、Restrict 参照整合性アクションを author フィールドに設定します。

これは、ユーザーが投稿を持っている場合、そのユーザー (およびその投稿) は削除できないことを意味します。

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()
.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)
})