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

リレーションのトラブルシューティング

スキーマのモデリングは、時に予期せぬ結果をもたらすことがあります。このセクションでは、その中で最も顕著なものをカバーすることを目的としています。

リレーションフィールドの順序が変更されると、暗黙的な多対多の自己リレーションが不正なデータを返す

問題

次の暗黙的な多対多の自己リレーションにおいて、a_eats (1) と b_eatenBy (2) のリレーションフィールドの辞書順

model Animal {
id Int @id @default(autoincrement())
name String
a_eats Animal[] @relation(name: "FoodChain")
b_eatenBy Animal[] @relation(name: "FoodChain")
}

結果として得られるSQLのリレーションテーブルは以下のようになります。ここで、A は被食者 (a_eats) を、B は捕食者 (b_eatenBy) を表します。

AB
8 (プランクトン)7 (サケ)
7 (サケ)9 (クマ)

次のクエリは、サケの被食者と捕食者を返します。

const getAnimals = await prisma.animal.findMany({
where: {
name: 'Salmon',
},
include: {
b_eats: true,
a_eatenBy: true,
},
})
表示クエリ結果
{
"id": 7,
"name": "Salmon",
"b_eats": [
{
"id": 8,
"name": "Plankton"
}
],
"a_eatenBy": [
{
"id": 9,
"name": "Bear"
}
]
}

ここで、リレーションフィールドの順序を変更します。

model Animal {
id Int @id @default(autoincrement())
name String
b_eats Animal[] @relation(name: "FoodChain")
a_eatenBy Animal[] @relation(name: "FoodChain")
}

変更をマイグレートし、Prisma Clientを再生成してください。更新されたフィールド名で同じクエリを実行すると、Prisma Clientは不正なデータ(サケがクマを食べ、プランクトンに食べられる)を返します。

const getAnimals = await prisma.animal.findMany({
where: {
name: 'Salmon',
},
include: {
b_eats: true,
a_eatenBy: true,
},
})
表示クエリ結果
{
"id": 1,
"name": "Salmon",
"b_eats": [
{
"id": 3,
"name": "Bear"
}
],
"a_eatenBy": [
{
"id": 2,
"name": "Plankton"
}
]
}

Prismaスキーマ内のリレーションフィールドの辞書順が変更されたにもかかわらず、データベース内のカラムAB変更されませんでした(名前が変更されたり、データが移動したりしていません)。したがって、Aは捕食者(a_eatenBy)を表し、Bは被食者(b_eats)を表します。

AB
8 (プランクトン)7 (サケ)
7 (サケ)9 (クマ)

解決策

暗黙的な多対多の自己リレーションでリレーションフィールドの名前を変更する場合は、フィールドのアルファベット順を維持するようにしてください。たとえば、`a_`と`b_`をプレフィックスとして使用します。

多対多リレーションでリレーションテーブルを使用する方法

多対多(m-n)リレーションを定義するには、暗黙的または明示的な方法がいくつかあります。暗黙的とは、Prisma ORMにリレーションテーブル(JOINテーブル)を内部で処理させることを意味し、各モデルの非スカラー型に対して配列/リストを定義するだけで済みます。詳細については、「暗黙的な多対多リレーション」を参照してください。

問題が発生する可能性があるのは、「明示的な多対多リレーションシップ」を作成する場合です。つまり、リレーションテーブルを自分で作成および処理する場合です。Prisma ORMがリレーションの両側の存在を要求することを見落とされがちです

次の例を見てください。ここでは、`Post`テーブルと`Category`テーブル間のJOINとして機能するリレーションテーブルが作成されています。しかし、リレーションテーブル(`PostCategories`)がそれぞれ他の2つのモデルと1対多のリレーションシップを形成する必要があるため、これは機能しません。

`Post`から`PostCategories`への、および`Category`から`PostCategories`への逆リレーションフィールドがモデルにありません。

// This example schema shows how NOT to define an explicit m-n relation

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] // This should refer to PostCategories
}

model PostCategories {
post Post @relation(fields: [postId], references: [id])
postId Int
category Category @relation(fields: [categoryId], references: [id])
categoryId Int
@@id([postId, categoryId])
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] // This should refer to PostCategories
}

これを修正するには、`Post`モデルに`PostCategories`リレーションテーブルを持つ多リレーションフィールドを定義する必要があります。`Category`モデルにも同様に適用されます。

これは、リレーションモデルが、結合している他の2つのモデルと1対多のリレーションシップを形成するためです。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[]
postCategories PostCategories[]
}

model PostCategories {
post Post @relation(fields: [postId], references: [id])
postId Int
category Category @relation(fields: [categoryId], references: [id])
categoryId Int

@@id([postId, categoryId])
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
postCategories PostCategories[]
}

多対多リレーションで@relation属性を使用する

暗黙的な多対多リレーションを構成する際に、モデルのリレーションフィールドに@relation("Post")アノテーションを追加するのは論理的に思えるかもしれません。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("Category")
Category Category? @relation("Post", fields: [categoryId], references: [id])
categoryId Int?
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("Post")
Post Post? @relation("Category", fields: [postId], references: [id])
postId Int?
}

しかし、これはPrisma ORMに2つの独立した1対多リレーションを期待するよう指示します。@relation属性の使用に関する詳細については、「リレーションの明確化」を参照してください。

次の例は、暗黙的な多対多リレーションを定義する正しい方法です。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("Category")
categories Category[]
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("Post")
posts Post[]
}

@relationアノテーションは、暗黙的な多対多リレーションで作成される基になるリレーションテーブルに名前を付けるためにも使用できます。

model Post {
id Int @id @default(autoincrement())
title String
categories Category[] @relation("CategoryPostRelation")
}

model Category {
id Int @id @default(autoincrement())
name String
posts Post[] @relation("CategoryPostRelation")
}

主キーが強制されるデータベースで多対多(m-n)リレーションを使用する

問題

一部のクラウドプロバイダーは、すべてのテーブルに主キーの存在を強制します。しかし、Prisma ORMが(@relationを介して表現される)暗黙的な構文を使用して多対多リレーションのために作成するリレーションテーブル(JOINテーブル)には主キーがありません。

解決策

明示的なリレーション構文を使用し、結合モデルを手動で作成し、その結合モデルに主キーがあることを確認する必要があります。

© . All rights reserved.