資料塑模
什麼是資料塑模?
資料塑模 這個詞指的是定義應用程式中物件的形狀和結構的過程,這些物件通常稱為「應用程式模型」。在關聯式資料庫(如 PostgreSQL)中,它們儲存在表格中。當使用文件資料庫(如 MongoDB)時,它們儲存在集合中。
根據您的應用程式領域,模型會有所不同。例如,如果您正在編寫部落格應用程式,您可能會有諸如 部落格、作者、文章 等模型。當編寫汽車共享應用程式時,您可能會有諸如 駕駛、汽車、路線 等模型。應用程式模型使您能夠通過建立各自的資料結構來表示程式碼中的這些不同實體。
在進行資料塑模時,您通常會問以下問題:
- 我的應用程式中的主要實體/概念是什麼?
- 它們之間有什麼關係?
- 它們的主要特徵/屬性是什麼?
- 如何使用我的技術堆疊來表示它們?
不使用 Prisma ORM 的資料塑模
資料塑模通常需要在(至少)兩個層級進行:
- 在資料庫層級
- 在應用程式層級(即,在您的程式語言中)
由於以下幾個原因,應用程式模型在這兩個層級的表示方式可能會有所不同:
- 資料庫和程式語言使用不同的資料類型
- 關聯在資料庫中的表示方式與在程式語言中的表示方式不同
- 資料庫通常具有更強大的資料塑模功能,例如索引、級聯刪除或各種額外的約束(例如,唯一、非空等)
- 資料庫和程式語言具有不同的技術限制
資料庫層級的資料塑模
關聯式資料庫
在關聯式資料庫中,模型由表格表示。例如,您可以定義一個 users
表格來儲存有關您的應用程式使用者的資訊。使用 PostgreSQL,您可以將其定義如下:
CREATE TABLE users (
user_id SERIAL PRIMARY KEY NOT NULL,
name VARCHAR(255),
email VARCHAR(255) UNIQUE NOT NULL,
isAdmin BOOLEAN NOT NULL DEFAULT false
);
users
表格及其一些隨機資料的視覺表示可能如下所示:
user_id | name | email | isAdmin |
---|---|---|---|
1 | Alice | alice@prisma.io | false |
2 | Bob | bob@prisma.io | false |
3 | Sarah | sarah@prisma.io | true |
它具有以下欄位:
user_id
:一個整數,隨著users
表格中的每個新紀錄而遞增。它也代表每個紀錄的主鍵。name
:一個最多 255 個字元的字串。email
:一個最多 255 個字元的字串。此外,新增的約束表示任何兩個紀錄都不能在email
欄位中具有重複的值,並且每個紀錄都需要為其提供一個值。isAdmin
:一個布林值,指示使用者是否具有管理員權限(預設值:false
)
MongoDB
在 MongoDB 資料庫中,模型由集合表示,並包含可以具有任何結構的文件
{
_id: '607ee94800bbe41f001fd568',
slug: 'prisma-loves-mongodb',
title: 'Prisma <3 MongoDB',
body: "This is my first post. Isn't MongoDB + Prisma awesome?!"
}
Prisma Client 目前期望一致的模型和正規化模型設計。這表示:
- 如果模型或欄位不存在於 Prisma schema 中,則會被忽略
- 如果欄位是強制性的,但不存在於 MongoDB 資料集中,您將會收到錯誤
應用程式層級的資料塑模
除了建立代表您的應用程式領域中實體的表格之外,您還需要在您的程式語言中建立應用程式模型。在物件導向語言中,這通常通過建立類別來表示您的模型來完成。根據程式語言的不同,這也可以通過介面或結構來完成。
資料庫中的表格與您在程式碼中定義的模型之間通常存在很強的關聯性。例如,為了表示應用程式中上述 users
表格中的紀錄,您可以定義一個類似於以下的 JavaScript (ES6) 類別:
class User {
constructor(user_id, name, email, isAdmin) {
this.user_id = user_id
this.name = name
this.email = email
this.isAdmin = isAdmin
}
}
當使用 TypeScript 時,您可以改為定義介面:
interface User {
user_id: number
name: string
email: string
isAdmin: boolean
}
請注意,這兩種情況下的 User
模型都具有與先前範例中 users
表格相同的屬性。雖然資料庫表格和應用程式模型之間通常存在 1:1 的對應關係,但也可能發生模型在資料庫和您的應用程式中以完全不同的方式表示的情況。
通過此設定,您可以從 users
表格中檢索紀錄,並將其儲存為 User
類型的實例。以下範例程式碼片段使用 pg
作為 PostgreSQL 的驅動程式,並根據上面定義的 JavaScript 類別建立 User
實例:
const resultRows = await client.query('SELECT * FROM users WHERE user_id = 1')
const userData = resultRows[0]
const user = new User(
userData.user_id,
userData.name,
userData.email,
userData.isAdmin
)
// user = {
// user_id: 1,
// name: "Alice",
// email: "alice@prisma.io",
// isAdmin: false
// }
請注意,在這些範例中,應用程式模型是「啞的」,這意味著它們不實作任何邏輯,但它們的唯一目的是攜帶資料作為普通的舊 JavaScript 物件。
使用 ORM 的資料塑模
ORM 通常在物件導向語言中使用,以使開發人員更容易使用資料庫。ORM 的主要特徵是它讓您可以根據類別來塑模您的應用程式資料,這些類別對應到基礎資料庫中的表格。
與上述方法相比,主要區別在於這些類別不僅攜帶資料,而且還實作了大量的邏輯。主要是用於儲存、檢索、序列化和反序列化,但有時它們也實作特定於您的應用程式的業務邏輯。
這意味著,您不編寫 SQL 語句來讀取和寫入資料庫中的資料,而是模型類別的實例提供了一個 API 來儲存和檢索資料。
Sequelize 是 Node.js 生態系統中流行的 ORM,以下是如何使用 Sequelize 的塑模方法從之前的章節中定義相同的 User
模型:
class User extends Model {}
User.init(
{
user_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING(255),
email: {
type: Sequelize.STRING(255),
unique: true,
},
isAdmin: Sequelize.BOOLEAN,
},
{ sequelize, modelName: 'user' }
)
為了使此 User
類別的範例能夠運作,您仍然需要在資料庫中建立相應的表格。使用 Sequelize,您有兩種方法可以做到這一點:
- 執行
User.sync()
(通常不建議用於生產環境) - 使用 Sequelize 遷移 來變更您的資料庫 schema
請注意,您永遠不會手動實例化 User
類別(使用 new User(...)
),如前一節所示,而是調用 User
類別上的靜態方法,然後這些方法會傳回 User
模型實例
const user = await User.findByPk(42)
對 findByPk
的調用會建立一個 SQL 語句,以檢索由 ID 值 42
識別的 User
紀錄。
產生的 user
物件是 Sequelize 的 Model
類別的實例(因為 User
繼承自 Model
)。它不是 POJO,而是一個實作了 Sequelize 的額外行為的物件。
使用 Prisma ORM 的資料塑模
根據您要在應用程式中使用 Prisma ORM 的哪些部分,資料塑模流程會略有不同。以下兩個章節說明了僅使用 Prisma Client 和使用 Prisma Client 和 Prisma Migrate 的工作流程。
無論採用哪種方法,使用 Prisma ORM,您都永遠不會通過手動定義類別、介面或結構在您的程式語言中建立應用程式模型。相反,應用程式模型在您的 Prisma schema 中定義
- 僅限 Prisma Client:Prisma schema 中的應用程式模型是根據您的資料庫 schema 的內省 (introspection) 生成的。資料塑模主要在資料庫層級進行。
- Prisma Client 和 Prisma Migrate:資料塑模在 Prisma schema 中進行,方法是手動將應用程式模型新增到其中。Prisma Migrate 將這些應用程式模型對應到基礎資料庫中的表格(目前僅支援關聯式資料庫)。
作為範例,先前範例中的 User
模型在 Prisma schema 中將表示如下:
model User {
user_id Int @id @default(autoincrement())
name String?
email String @unique
isAdmin Boolean @default(false)
}
一旦應用程式模型位於您的 Prisma schema 中(無論它們是通過內省新增的還是由您手動新增的),下一步通常是生成 Prisma Client,它提供了一個程式化的且類型安全的 API,用於以您的應用程式模型的形狀讀取和寫入資料。
Prisma Client 使用 TypeScript 類型別名 來表示程式碼中的應用程式模型。例如,User
模型將在生成的 Prisma Client 庫中表示如下:
export declare type User = {
id: number
name: string | null
email: string
isAdmin: boolean
}
除了生成的類型之外,Prisma Client 還提供了一個資料存取 API,您可以在安裝 @prisma/client
套件後使用它
import { PrismaClient } from '@prisma/client'
// or
// const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
// use inside an `async` function to `await` the result
await prisma.user.findUnique(...)
await prisma.user.findMany(...)
await prisma.user.create(...)
await prisma.user.update(...)
await prisma.user.delete(...)
await prisma.user.upsert(...)
僅使用 Prisma Client
當僅使用 Prisma Client 且不在您的應用程式中使用 Prisma Migrate 時,資料塑模需要通過 SQL 在資料庫層級進行。一旦您的 SQL schema 準備就緒,您可以使用 Prisma 的內省 (introspection) 功能將應用程式模型新增到您的 Prisma schema。最後,您生成 Prisma Client,它會為您建立類型以及程式化的 API,以便在您的資料庫中讀取和寫入資料。
以下是主要工作流程的概述:
- 使用 SQL 變更您的資料庫 schema(例如
CREATE TABLE
、ALTER TABLE
等) - 執行
prisma db pull
以內省 (introspect) 資料庫並將應用程式模型新增到 Prisma schema - 執行
prisma generate
以更新您的 Prisma Client API
使用 Prisma Client 和 Prisma Migrate
當使用 Prisma Migrate 時,您可以在 Prisma schema 中定義您的應用程式模型,並使用關聯式資料庫使用 prisma migrate
子命令來生成純 SQL 遷移檔案,您可以在應用之前編輯這些檔案。使用 MongoDB,您改用 prisma db push
,它會將變更直接應用到您的資料庫。
以下是主要工作流程的概述:
- 手動變更 Prisma schema 中的應用程式模型(例如,新增模型、移除現有模型等)
- 執行
prisma migrate dev
以建立和應用遷移,或執行prisma db push
以直接應用變更(在這兩種情況下,Prisma Client 都會自動生成)