一対多リレーション
このページでは、一対多リレーションについて紹介し、Prismaスキーマでそれらを使用する方法を説明します。
概要
一対多(1対n)リレーションとは、リレーションの一方の側のレコードが、もう一方の側のゼロ個以上のレコードに接続できるリレーションを指します。以下の例では、User
モデルとPost
モデルの間に一対多リレーションが1つあります
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
}
注記
posts
フィールドは、基となるデータベーススキーマには「現れません」。リレーションのもう一方の側では、アノテーション付きリレーションフィールドauthor
とそのリレーションスカラーauthorId
が、基となるデータベースに外部キーを格納するリレーションの側を表します。
この一対多リレーションは以下を表します
- 「ユーザーはゼロ個以上の投稿を持つことができる」
- 「投稿には常に作成者が必要である」
前の例では、Post
モデルのauthor
リレーションフィールドが、User
モデルのid
フィールドを参照しています。異なるフィールドを参照することも可能です。この場合、各Post
に接続されるUser
が1つだけであることを保証するために、そのフィールドに@unique
属性をマークする必要があります。以下の例では、author
フィールドがUser
モデルのemail
フィールドを参照しており、これは@unique
属性でマークされています
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
email String @unique // <-- add unique attribute
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
authorEmail String
author User @relation(fields: [authorEmail], references: [email])
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique // <-- add unique attribute
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
authorEmail String
author User @relation(fields: [authorEmail], references: [email])
}
MySQLでは、参照される側にインデックスのみを持つ外部キーを作成でき、一意性制約は不要です。Prisma ORMバージョン4.0.0以降では、このタイプのリレーションをイントロスペクトすると検証エラーがトリガーされます。これを修正するには、参照されるフィールドに@unique
制約を追加する必要があります。
リレーショナルデータベースにおける複数フィールドリレーション
リレーショナルデータベースのみで、このリレーションを複数フィールドID/複合キーを使用して定義することもできます
model User {
firstName String
lastName String
post Post[]
@@id([firstName, lastName])
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorFirstName, authorLastName], references: [firstName, lastName])
authorFirstName String // relation scalar field (used in the `@relation` attribute above)
authorLastName String // relation scalar field (used in the `@relation` attribute above)
}
データベースにおける1対nリレーション
リレーショナルデータベース
以下の例は、SQLで1対nリレーションを作成する方法を示しています
CREATE TABLE "User" (
id SERIAL PRIMARY KEY
);
CREATE TABLE "Post" (
id SERIAL PRIMARY KEY,
"authorId" integer NOT NULL,
FOREIGN KEY ("authorId") REFERENCES "User"(id)
);
authorId
カラム(外部キー)にUNIQUE
制約がないため、**同じUser
レコードを指す複数のPost
レコード**を作成できます。これにより、リレーションは一対一ではなく一対多になります。
以下の例は、複合キー(firstName
とlastName
)を使用してSQLで1対nリレーションを作成する方法を示しています
CREATE TABLE "User" (
firstName TEXT,
lastName TEXT,
PRIMARY KEY ("firstName","lastName")
);
CREATE TABLE "Post" (
id SERIAL PRIMARY KEY,
"authorFirstName" TEXT NOT NULL,
"authorLastName" TEXT NOT NULL,
FOREIGN KEY ("authorFirstName", "authorLastName") REFERENCES "User"("firstName", "lastName")
);
一対一リレーションと一対多リレーションの比較
リレーショナルデータベースでは、1対1リレーションと1対nリレーションの主な違いは、1対1リレーションでは外部キーにUNIQUE
制約が定義されている必要があることです。
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しており、これはドキュメントがリレーショナルデータベースと同様にIDで互いを参照することを意味します。
以下のMongoDBドキュメントはUser
を表します
{ "_id": { "$oid": "60d5922d00581b8f0062e3a8" }, "name": "Ella" }
以下の各Post
MongoDBドキュメントには、同じユーザーを参照するauthorId
フィールドがあります
[
{
"_id": { "$oid": "60d5922e00581b8f0062e3a9" },
"title": "How to make sushi",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
},
{
"_id": { "$oid": "60d5922e00581b8f0062e3aa" },
"title": "How to re-install Windows",
"authorId": { "$oid": "60d5922d00581b8f0062e3a8" }
}
]
一対一リレーションと一対多リレーションの比較
MongoDBでは、1対1リレーションと1対nリレーションの唯一の違いは、データベース内で別のドキュメントを参照するドキュメントの数です。制約はありません。
一対多リレーションにおける必須およびオプションのリレーションフィールド
1対nリレーションには常に2つのリレーションフィールドがあります
@relation
でアノテーションが*付いていない*リストリレーションフィールド- アノテーション付きリレーションフィールド(そのリレーションスカラーを含む)
1対nリレーションのアノテーション付きリレーションフィールドとリレーションスカラーは、両方ともオプションであるか、両方とも必須であるかのいずれかです。リレーションのもう一方の側では、リストは**常に必須**です。
オプションの一対多リレーション
以下の例では、User
を割り当てずにPost
を作成できます
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User? @relation(fields: [authorId], references: [id])
authorId String? @db.ObjectId
}
必須の一対多リレーション
以下の例では、Post
を作成する際にUser
を割り当てる必要があります。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
posts Post[]
}
model Post {
id String @id @default(auto()) @map("_id") @db.ObjectId
author User @relation(fields: [authorId], references: [id])
authorId String @db.ObjectId
}