iT邦幫忙

2024 iThome 鐵人賽

DAY 11
1
Modern Web

一些讓你看來很強的 ORM - prisma系列 第 11

Day11. 一些讓你看來很強的 ORM - prisma (relation)1-1

  • 分享至 

  • xImage
  •  

今天我們要來繼續講 relationprisma 中總共有三種 relation type :

  • One-to-one (1-1 relations)
  • One-to-many (1-n relations)
  • Many-to-many (m-n relations)

以下方的例子為例

  • one-to-one: User ↔ Profile
  • one-to-many: User ↔ Post
  • many-to-many: Post ↔ Category
model User {
  id      Int      @id @default(autoincrement())
  posts   Post[]
  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 Post {
  id         Int        @id @default(autoincrement())
  author     User       @relation(fields: [authorId], references: [id])
  authorId   Int // relation scalar field  (used in the `@relation` attribute above)
  categories Category[]
}

model Category {
  id    Int    @id @default(autoincrement())
  posts Post[]
}

彼此的關聯圖如下,接下來的幾天我們將更深入瞭解 relationprisma 中的實作
https://ithelp.ithome.com.tw/upload/images/20240925/20145677LTLsJaQvCI.png

One-to-one relations

One-to-one (1-1) relations 代表著是一個 record 可以關聯到另外一個 modelrelation,如以下的範例,一個 User 只會有一個 profile,同時你會看到因為 ? 的關係 profileoptional ,所以 User 可以沒有 Profile

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

User.userId 他代表著是一個 scalar type ,對於 SQL 來說是一個 foreign key

以上一個範例來說 user.id 關聯 Profile.userId ,但其實你不一定要這樣關聯,只要其他的欄位確保是 uniqueok ,所以我們在 User 加上 uniqueemail ,然後調整 Profile.userrelationreferencesUser.email,因為 @unique 關係,一個 User 只會有一個 email ,所以也可以保證 User.profile 的唯一性

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

另一個比較有趣的是,我們不一定只能透過 id 來當作 record 的唯一性,除了上面看到的 @unique 以外你也可以透過 composite id 的方式,讓兩個 filed 具備唯一性的特點,這邊我們打算以 firstNamelastName 為例

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])
}

另外這邊要注意的是 Profile 也需要確保 @relation.fields 也是 unique 的這樣關聯起來的資料才不會有重複的情況
https://ithelp.ithome.com.tw/upload/images/20240925/201456772I61mRk2vB.png

1-1 relations in the database

以上面 userId 的範例來說在 SQL 會長這樣,這邊要注意的是如果你是透過 SQL 直接 create table 的話,記得 userId 要加上 UNIQUE 否則會被認為是一對多的關係~

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)
);

那如果是 firstName + lastNameid 的範例 SQL 如下,一樣因為是一對一的關係要記得加上 UNIQUE

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")
);

Required and optional

在一對一的情況中,如果你的 User model 沒有加上 @relation 欄位,你關聯的 filed 只能是 ? optional

model User {
  id      Int      @id @default(autoincrement())
  profile Profile? // No relation scalar - must be optional
}

那如果你要強制 User 一定要有一個 profile 則必須要加上 @relation ,如此在 create user 的時候,只必須要關聯到一筆 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?
}

也或者 @relation 可以加上 ? 這樣當你 create user 也不會強制你要 connectprofile

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?
}

最後補充一個概念就是,因為是一對一的關西,今天你要定義 @relation 在哪個 model 都可以,沒有一定的答案,全看你怎麼設計

model User {
  id        Int      @id @default(autoincrement())
  profile   Profile?
}

model Profile{
  id        Int      @id @default(autoincrement())
  user   User  @relation(fileds:[userId],reference:[id])
  userId Int  @unique
}
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?
}

大家如果有問題可以來小弟的群組討論~

✅ 前端社群 :
https://lihi3.cc/kBe0Y


上一篇
Day10. 一些讓你看來很強的 ORM - prisma (relation)介紹
下一篇
Day12. 一些讓你看來很強的 ORM - prisma (relation)1-n
系列文
一些讓你看來很強的 ORM - prisma30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言