1対1のリレーション
このページでは、1対1のリレーションについて紹介し、Prismaスキーマでの使用方法を説明します。
概要
1対1(1-1)リレーションとは、リレーションの両側で接続できるレコードが最大で**1つ**であるリレーションを指します。以下の例では、User
とProfile
の間に1対1のリレーションがあります。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
profile Profile?
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int @unique // relation scalar field (used in the `@relation` attribute above)
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
userId
リレーションスカラーは、基盤となるデータベースにおける外部キーの直接的な表現です。この1対1リレーションは、以下を表現しています。
- "ユーザーはゼロ個または1個のプロファイルを持つことができる" (
profile
フィールドがUser
でオプションであるため) - "プロファイルは常に1人のユーザーに接続されている必要がある"
前の例では、Profile
モデルのuser
リレーションフィールドがUser
モデルのid
フィールドを参照していました。異なるフィールドを参照することも可能です。その場合、各Profile
に接続されるUser
が1つのみであることを保証するために、そのフィールドに@unique
属性をマークする必要があります。以下の例では、user
フィールドはUser
モデルのemail
フィールドを参照しており、このemail
フィールドには@unique
属性がマークされています。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
email String @unique // <-- add unique attribute
profile Profile?
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userEmail], references: [email])
userEmail String @unique // relation scalar field (used in the `@relation` attribute above)
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
email String @unique // <-- add unique attribute
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userEmail], references: [email])
userEmail String @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
MySQLでは、参照側で一意制約ではなく、インデックスのみを持つ外部キーを作成できます。Prisma ORMバージョン4.0.0以降では、このタイプのリレーションをイントロスペクトすると検証エラーが発生します。これを修正するには、参照されるフィールドに@unique
制約を追加する必要があります。
リレーショナルデータベースにおける複数フィールドリレーション
**リレーショナルデータベースのみ**で、複数フィールドIDを使用して1対1のリレーションを定義することもできます。
model User {
firstName String
lastName String
profile Profile?
@@id([firstName, lastName])
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userFirstName, userLastName], references: [firstName, lastName])
userFirstName String // relation scalar field (used in the `@relation` attribute above)
userLastName String // relation scalar field (used in the `@relation` attribute above)
@@unique([userFirstName, userLastName])
}
データベースにおける1対1リレーション
リレーショナルデータベース
以下の例は、SQLで1対1リレーションを作成する方法を示しています。
CREATE TABLE "User" (
id SERIAL PRIMARY KEY
);
CREATE TABLE "Profile" (
id SERIAL PRIMARY KEY,
"userId" INTEGER NOT NULL UNIQUE,
FOREIGN KEY ("userId") REFERENCES "User"(id)
);
外部キーuserId
にはUNIQUE
制約があることに注意してください。このUNIQUE
制約がない場合、そのリレーションは1対多のリレーションと見なされます。
以下の例は、複合キー(firstName
とlastName
)を使用してSQLで1対1リレーションを作成する方法を示しています。
CREATE TABLE "User" (
firstName TEXT,
lastName TEXT,
PRIMARY KEY ("firstName","lastName")
);
CREATE TABLE "Profile" (
id SERIAL PRIMARY KEY,
"userFirstName" TEXT NOT NULL,
"userLastName" TEXT NOT NULL,
UNIQUE ("userFirstName", "userLastName")
FOREIGN KEY ("userFirstName", "userLastName") REFERENCES "User"("firstName", "lastName")
);
MongoDB
MongoDBの場合、Prisma ORMは現在、正規化されたデータモデル設計を使用しています。これは、ドキュメントがリレーショナルデータベースと同様の方法でIDによって相互を参照することを意味します。
以下のMongoDBドキュメントはUser
を表しています。
{ "_id": { "$oid": "60d58e130011041800d209e1" }, "name": "Bob" }
以下のMongoDBドキュメントはProfile
を表しています - User
ドキュメントの$oid
を参照するuserId
フィールドに注目してください。
{
"_id": { "$oid": "60d58e140011041800d209e2" },
"bio": "I'm Bob, and I like drawing.",
"userId": { "$oid": "60d58e130011041800d209e1" }
}
必須およびオプションの1対1リレーションフィールド
1対1のリレーションでは、リレーションスカラー(データベースにおける外部キーを表すフィールド)を**持たない**側のリレーションフィールドは、**必ず**オプションである必要があります。
model User {
id Int @id @default(autoincrement())
profile Profile? // No relation scalar - must be optional
}
この制約は2.12.0で導入されました。
ただし、リレーションスカラーを**持つ**側のリレーションがオプションであるか必須であるかは選択できます。
必須の1対1リレーション
以下の例では、profile
とprofileId
は必須です。これは、Profile
を接続または作成せずにUser
を作成することはできないことを意味します。
model User {
id Int @id @default(autoincrement())
profile Profile @relation(fields: [profileId], references: [id]) // references `id` of `Profile`
profileId Int @unique // relation scalar field (used in the `@relation` attribute above)
}
model Profile {
id Int @id @default(autoincrement())
user User?
}
オプションの1対1リレーション
以下の例では、profile
とprofileId
はオプションです。これは、Profile
を接続または作成せずにユーザーを作成できることを意味します。
model User {
id Int @id @default(autoincrement())
profile Profile? @relation(fields: [profileId], references: [id]) // references `id` of `Profile`
profileId Int? @unique // relation scalar field (used in the `@relation` attribute above)
}
model Profile {
id Int @id @default(autoincrement())
user User?
}
1対1リレーションで外部キーをどちらの側に格納するかを選択する
**1対1リレーション**では、リレーションのどちらの側に@relation
属性をアノテーションするか(したがって、外部キーを保持するか)を自分で決定できます。
以下の例では、Profile
モデルのリレーションフィールドに@relation
属性がアノテーションされています。userId
は、基盤となるデータベースにおける外部キーの直接的な表現です。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
profile Profile?
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id])
userId Int @unique // relation scalar field (used in the `@relation` attribute above)
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
profile Profile?
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @unique @db.ObjectId
}
リレーションの反対側にも@relation
属性をアノテーションできます。以下の例では、User
モデルのリレーションフィールドにアノテーションされています。profileId
は、基盤となるデータベースにおける外部キーの直接的な表現です。
- リレーショナルデータベース
- MongoDB
model User {
id Int @id @default(autoincrement())
profile Profile? @relation(fields: [profileId], references: [id])
profileId Int? @unique // relation scalar field (used in the `@relation` attribute above)
}
model Profile {
id Int @id @default(autoincrement())
user User?
}
model User {
id String @id @default(auto()) @map("_id") @db.ObjectId
profile Profile? @relation(fields: [profileId], references: [id])
profileId String? @unique @db.ObjectId // relation scalar field (used in the `@relation` attribute above)
}
model Profile {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User?
}