今天的要來介紹 Self Relations
,第一次聽到這名詞的你可能會覺得很酷, relation
除了一對一、一對多、多對多外,自己原來也可以 relation
自己,會有 Self Relations
的出現原因是,我們的業務需求中,對於 model
的使用上,會有同一個 model
但是不同解讀的方式,舉個例子以 teacher
跟 student
來說,他們的 model
都是屬於 User
,同時學生可能會有他的老師,相反老師可能也會有自己的學生,所以這個情形就很適合用 Self Relations
去描敘關聯的關係,Self Relations
的結構上,很像是一個上下層的概念去 chain
在一起, 很特別的是他可以用在 1-1
、1-n
、m-n
中,這邊我們將慢慢介紹~
這邊就是一個單純的一對一的關係,你會看到:
user
可能會有 0 個或一個前輩user
可能會有 0 個或一個繼任者這邊要注意的是,如果是 1-1
的 Self-Relations
兩邊的 relation
不能都是 required
因為這樣你會無法 create
第一筆 User
,1-1
必需要先有 user list
後,你才能用類似於 linklist
的方式,去指定該 User
的上下關係,並 connect
這位 User
的前輩是誰繼任者是誰
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
這邊有幾個小規則:
relation
必須要有相同的 name
例如這邊的 BlogOwnerHistory
relation
要完整描述,relation
的全部內容1-1
的寫法一樣,其中一邊要當作 foreign key
去關聯到哪個 User
這邊來說就是 successorId
同時被定義成 foreign key
的欄位,這邊來說就是 successorId
,必須要是 unique
的以確保 1-1
的唯一性
model User {
id Int @id @default(autoincrement())
name String?
successorId Int? @unique
successor User? @relation("BlogOwnerHistory", fields: [successorId], references: [id])
predecessor User? @relation("BlogOwnerHistory")
}
那因為是 1-1
所以你要反過來讓 predecessorId
當作 foreign key
也是 OK
一樣你需要確保 predecessorId
的 unique
model User {
id Int @id @default(autoincrement())
name String?
successor User? @relation("BlogOwnerHistory")
predecessorId Int? @unique
predecessor User? @relation("BlogOwnerHistory", fields: [predecessorId], references: [id])
}
那不管你是 predecessorId
或是 successorId
當你的 foreign key
,在 prisma client
寫法都是一樣的~
const x = await prisma.user.create({
data: {
name: "Bob McBob",
successor: {
connect: {
id: 2,
},
},
predecessor: {
connect: {
id: 4,
},
},
},
});
以下是 SQL
對於 1-1
Self Relation
的語法,跟 1-1
的 SQL
一樣的是需要對 successorId
加上 Unique
的 CONSTRAINT
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"successorId" INTEGER
);
ALTER TABLE "User" ADD CONSTRAINT fk_successor_user FOREIGN KEY ("successorId") REFERENCES "User" (id);
ALTER TABLE "User" ADD CONSTRAINT successor_unique UNIQUE ("successorId");
這邊的 1-n
的關係會是
student
可能會有 0 個或一個 teacher
teacher
可能會有 0 個或多個 student
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
}
那跟 1-n
的 SQL
一樣,不需要在 teacherId
加上 unique
,因為多個學生,可能會給同一個 teacher
教到~
CREATE TABLE "User" (
id SERIAL PRIMARY KEY,
"name" TEXT,
"teacherId" INTEGER
);
ALTER TABLE "User" ADD CONSTRAINT fk_teacherid_user FOREIGN KEY ("teacherId") REFERENCES "User" (id);
這邊的 m-n
的關係會是
user
可以被 followed
0 到多個 user
user
可能會去 follow
0 到多個 user
這邊跟 m-n
的寫法一樣都適用 implicit
,prisma
自動幫你管理中間表的部分
model User {
id Int @id @default(autoincrement())
name String?
followedBys User[] @relation("UserFollows")
followings User[] @relation("UserFollows")
}
那如果需要管理中間表的話可以用 explicit mode
~
model User {
id Int @id @default(autoincrement())
name String?
followedBys Follows[] @relation("followedBy")
followings Follows[] @relation("following")
}
model Follows {
followedBy User @relation("followedBy", fields: [followedById], references: [id])
followedById Int
following User @relation("following", fields: [followingId], references: [id])
followingId Int
@@id([followingId, followedById])
}
對應的 SQL
語法如下
CREATE TABLE "User" (
id integer DEFAULT nextval('"User_id_seq"'::regclass) PRIMARY KEY,
name text
);
CREATE TABLE "_UserFollows" (
"A" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE,
"B" integer NOT NULL REFERENCES "User"(id) ON DELETE CASCADE ON UPDATE CASCADE
);
在 prisma client
的寫法如下~你會發現不管是 implicit
或是 explicit
都跟之前的寫法一樣,只是變成兩個欄位 followedBys
跟 followings
去 connect
//implicit
const newUser = await prismaClient.user.create({
data: {
name: "Danny3.0",
followedBys: {
connect: {
id: 1
}
},
followings: {
connect: [
{ id: 5 }, { id: 6 }, { id: 7 }
]
}
}
})
//explicit
const newUser = await prismaClient.user.create({
data: {
name: "Danny3.0",
followedBys: {
create: [{
followedBy: {
connect: {
id: 1
}
}
}]
},
followings: {
createMany: {
data: [
{ followingId: 5 },
{ followingId: 6 },
{ followingId: 7 },
]
}
}
}
})
Self Relation
很特別的是可以同時寫 1-1
、1-n
、m-n
情況在同一個 model
中
model User {
id Int @id @default(autoincrement())
name String?
teacherId Int?
teacher User? @relation("TeacherStudents", fields: [teacherId], references: [id])
students User[] @relation("TeacherStudents")
followedBy User[] @relation("UserFollows")
following User[] @relation("UserFollows")
}
✅ 前端社群 :
https://lihi3.cc/kBe0Y