如何從 Drizzle 遷移到 Prisma ORM
簡介
本指南將向您展示如何將應用程式從 Drizzle 遷移到 Prisma ORM。我們將使用一個基於 Drizzle Next.js 示例 的示例專案來演示遷移步驟。您可以在 GitHub 上找到本指南使用的示例。
您可以在 Prisma ORM 與 Drizzle 比較頁面瞭解 Prisma ORM 與 Drizzle 的對比。
先決條件
在開始本指南之前,請確保您已具備以下條件:
- 要遷移的 Drizzle 專案
- 已安裝 Node.js(版本 16 或更高)
- PostgreSQL 或其他支援的資料庫
- 對 Drizzle 和 Next.js 有基本瞭解
本遷移指南使用 Neon PostgreSQL 作為示例資料庫,但它同樣適用於 Prisma ORM 支援的任何其他關係型資料庫。
您可以在 Prisma ORM 與 Drizzle 比較頁面瞭解 Prisma ORM 與 Drizzle 的對比。
遷移過程概述
請注意,無論您正在構建何種應用程式或 API 層,從 Drizzle 遷移到 Prisma ORM 的步驟始終相同:
- 安裝 Prisma CLI
- 內省您的資料庫
- 建立基線遷移
- 安裝 Prisma Client
- 逐步將您的 Drizzle 查詢替換為 Prisma Client
這些步驟均適用,無論您是構建 REST API(例如使用 Express、Koa 或 NestJS)、GraphQL API(例如使用 Apollo Server、TypeGraphQL 或 Nexus)還是任何其他使用 Drizzle 訪問資料庫的應用程式。
Prisma ORM 非常適合**增量採用**。這意味著,您不必一次性將整個專案從 Drizzle 遷移到 Prisma ORM,而是可以**逐步**將資料庫查詢從 Drizzle 轉移到 Prisma ORM。
步驟 1. 安裝 Prisma CLI
採用 Prisma ORM 的第一步是在您的專案中安裝 Prisma CLI。
npm install prisma --save-dev && npm install @prisma/client
步驟 2. 內省您的資料庫
2.1. 設定 Prisma ORM
在內省資料庫之前,您需要設定您的 Prisma schema 並將 Prisma 連線到您的資料庫。在專案根目錄執行以下命令以建立基本的 Prisma schema 檔案:
npx prisma init --output ../generated/prisma
此命令為您建立了一個名為 prisma 的新目錄,其中包含以下檔案:
schema.prisma:您的 Prisma schema,用於指定資料庫連線和模型.env:一個用於將資料庫連線 URL 配置為環境變數的dotenv檔案
您可能已經有一個 .env 檔案。如果存在,prisma init 命令將向其追加內容而不是建立新檔案。
Prisma schema 當前如下所示:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
如果您使用 VS Code,請務必安裝 Prisma VS Code 擴充套件,以獲得語法高亮、格式化、自動補全以及更多炫酷功能。
2.2. 連線您的資料庫
如果您不使用 PostgreSQL,則需要調整 datasource 塊中的 provider 欄位,以匹配您當前使用的資料庫:
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
完成後,您可以在 .env 檔案中配置您的 資料庫連線 URL。Drizzle 和 Prisma ORM 使用相同的連線 URL 格式,因此您現有的連線 URL 應該可以正常工作。
2.3. 使用 Prisma ORM 內省您的資料庫
配置好連線 URL 後,您可以內省資料庫以生成 Prisma 模型。
npx prisma db pull
如果您使用示例專案,將建立以下模型:
model todo {
id Int @id
text String
done Boolean @default(false)
}
生成的 Prisma 模型代表一個數據庫表。Prisma 模型是您程式化 Prisma Client API 的基礎,它允許您向資料庫傳送查詢。
2.4. 建立基線遷移
要繼續使用 Prisma Migrate 來演進資料庫 schema,您需要為資料庫建立基線。
首先,建立一個 migrations 目錄,並在其中新增一個子目錄,名稱為您偏好的遷移名稱。在本例中,我們將使用 0_init 作為遷移名稱:
mkdir -p prisma/migrations/0_init
接下來,使用 prisma migrate diff 生成遷移檔案。使用以下引數:
--from-empty:假定您要遷移的資料模型為空--to-schema-datamodel:使用datasource塊中 URL 的當前資料庫狀態--script:輸出 SQL 指令碼
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
檢查生成的遷移以確保一切正確。
接下來,使用帶有 --applied 引數的 prisma migrate resolve 命令將遷移標記為已應用。
npx prisma migrate resolve --applied 0_init
該命令將透過將其新增到 _prisma_migrations 表來標記 0_init 為已應用。
您現在已經為當前資料庫 schema 建立了基線。要對資料庫 schema 進行進一步更改,您可以更新 Prisma schema 並使用 prisma migrate dev 將更改應用到資料庫。
2.5. 調整 Prisma schema(可選)
透過內省生成的模型目前與資料庫表精確對映。在本節中,您將學習如何調整 Prisma 模型的命名,以符合 Prisma ORM 的命名約定。
所有這些調整都是完全可選的,如果您暫時不想進行任何調整,可以自由地跳到下一步。您可以在以後的任何時候回來進行調整。
與 Drizzle 模型當前使用的 camelCase 命名法不同,Prisma ORM 的命名約定是:
- 模型名稱使用 PascalCase
- 欄位名稱使用 camelCase
您可以透過使用 @@map 和 @map 將 Prisma 模型和欄位名稱對映到底層資料庫中現有的表和列名稱來調整命名。
以下是如何修改上述模型的一個示例:
model Todo {
id Int @id
text String
done Boolean @default(false)
@@map("todo")
}
步驟 3. 安裝並生成 Prisma Client
下一步,您可以在專案中安裝 Prisma Client,這樣就可以開始替換專案中當前使用 Drizzle 進行的資料庫查詢了。
npm install @prisma/client
安裝後,您需要執行 generate 以使您的 schema 反映在 TypeScript 型別和自動補全中。
npx prisma generate
步驟 4. 使用 Prisma Client 替換您的 Drizzle 查詢
在本節中,我們將基於示例 REST API 專案的示例路由,展示一些從 Drizzle 遷移到 Prisma Client 的示例查詢。有關 Prisma Client API 與 Drizzle 之間差異的全面概述,請檢視比較頁面。
首先,設定 PrismaClient 例項,您將使用它從各種路由處理程式傳送資料庫查詢。在 db 目錄中建立一個名為 prisma.ts 的新檔案:
touch db/prisma.ts
現在,例項化 PrismaClient 並從檔案中匯出它,以便您以後可以在路由處理程式中使用它。
import { PrismaClient } from '@prisma/client'
export const prisma = new PrismaClient()
4.1. 替換 getData 查詢
全棧 Next.js 應用程式有多個操作,包括 getData。
getData 操作目前的實現如下:
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const getData = async () => {
const data = await db.select().from(todo);
return data;
};
這是使用 Prisma Client 實現的相同操作:
import { prisma } from "@/db/prisma";
export const getData = async () => {
const data = await prisma.todo.findMany();
return data;
};
4.2. 替換 POST 請求中的查詢
示例專案 有四個在 POST 請求期間使用的操作:
addTodo:建立新的Todo記錄deleteTodo:刪除現有Todo記錄toggleTodo:切換現有Todo記錄上的布林型done欄位editTodo:編輯現有Todo記錄上的text欄位
addTodo
addTodo 操作目前的實現如下:
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const addTodo = async (id: number, text: string) => {
await db.insert(todo).values({
id: id,
text: text,
});
revalidatePath("/");
};
這是使用 Prisma Client 實現的相同操作:
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const addTodo = async (id: number, text: string) => {
await prisma.todo.create({
data: { id, text },
})
revalidatePath("/");
};
deleteTodo
deleteTodo 操作目前的實現如下:
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const deleteTodo = async (id: number) => {
await db.delete(todo).where(eq(todo.id, id));
revalidatePath("/");
};
這是使用 Prisma Client 實現的相同操作:
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const deleteTodo = async (id: number) => {
await prisma.todo.delete({ where: { id } });
revalidatePath("/");
};
toggleTodo
ToggleTodo 操作目前的實現如下:
import { eq, not } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const toggleTodo = async (id: number) => {
await db
.update(todo)
.set({
done: not(todo.done),
})
.where(eq(todo.id, id));
revalidatePath("/");
};
這是使用 Prisma Client 實現的相同操作:
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const toggleTodo = async (id: number) => {
const todo = await prisma.todo.findUnique({ where: { id } });
if (todo) {
await prisma.todo.update({
where: { id: todo.id },
data: { done: !todo.done },
})
revalidatePath("/");
}
};
請注意,Prisma ORM 不具備“原地”編輯布林欄位的能力,因此必須事先獲取記錄。
editTodo
editTodo 操作目前的實現如下:
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import db from "@/db/drizzle";
import { todo } from "@/db/schema";
export const editTodo = async (id: number, text: string) => {
await db
.update(todo)
.set({
text: text,
})
.where(eq(todo.id, id));
revalidatePath("/");
};
這是使用 Prisma Client 實現的相同操作:
import { revalidatePath } from "next/cache";
import { prisma } from "@/db/prisma";
export const editTodo = async (id: number, text: string) => {
await prisma.todo.update({
where: { id },
data: { text },
})
revalidatePath("/");
};
更多
隱式多對多關係
與 Drizzle 不同,Prisma ORM 允許您隱式建模多對多關係。也就是說,在多對多關係中,您無需在 schema 中**顯式**管理關係表(有時也稱為 JOIN 表)。以下是 Drizzle 與 Prisma ORM 的比較示例:
import { boolean, integer, pgTable, serial, text } from "drizzle-orm/pg-core";
export const posts = pgTable('post', {
id: serial('serial').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false).notNull(),
});
export const categories = pgTable('category', {
id: serial('serial').primaryKey(),
name: text('name').notNull(),
});
export const postsToCategories = pgTable('posts_to_categories', {
postId: integer('post_id').notNull().references(() => users.id),
categoryId: integer('category_id').notNull().references(() => chatGroups.id),
});
此 schema 等同於以下 Prisma schema:
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
postsToCategories PostToCategories[]
@@map("post")
}
model Category {
id Int @id @default(autoincrement())
name String
postsToCategories PostToCategories[]
@@map("category")
}
model PostToCategories {
postId Int
categoryId Int
category Category @relation(fields: [categoryId], references: [id])
post Post @relation(fields: [postId], references: [id])
@@id([postId, categoryId])
@@index([postId])
@@index([categoryId])
@@map("posts_to_categories")
}
在此 Prisma schema 中,多對多關係透過關係表 PostToCategories **顯式**建模。
如果轉而遵循 Prisma ORM 關係表的約定,關係可以如下所示:
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
categories Category[]
}
model Category {
id Int @id @default(autoincrement())
name String
posts Post[]
}
這也將使 Prisma Client API 更符合人體工程學且更簡潔,用於修改此關係中的記錄,因為您可以從 Post 直接訪問 Category(反之亦然),而無需首先遍歷 PostToCategories 模型。
如果您的資料庫提供商要求表具有主鍵,則必須使用顯式語法,並手動建立帶有主鍵的連線模型。這是因為 Prisma ORM(透過 @relation 表達)為使用隱式語法的多對多關係建立的關係表(JOIN 表)不具有主鍵。
與 Prisma 保持聯絡
透過以下方式與我們保持聯絡,繼續您的 Prisma 之旅: 我們活躍的社群。保持資訊靈通,積極參與,並與其他開發者協作
- 在 X 上關注我們 獲取公告、即時活動和實用技巧。
- 加入我們的 Discord 提問、與社群交流,並透過對話獲得積極支援。
- 在 YouTube 上訂閱 獲取教程、演示和直播。
- 在 GitHub 上參與 透過點贊倉庫、報告問題或貢獻程式碼。