動(dòng):安裝、連接與遷移完整指南)
Drizzle ORM 接入 Postgres.js 驅(qū)動(dòng)安裝、連接與遷移完整指南【免費(fèi)下載鏈接】drizzle-ormORM項(xiàng)目地址: https://gitcode.com/gh_mirrors/dr/drizzle-ormDrizzle ORM 為 PostgreSQL 提供了多種驅(qū)動(dòng)適配層其中基于 Postgres.js 為核心骨架結(jié)合drizzle-orm/src/postgres-js/目錄下的真實(shí)源碼driver/session/migrator完整講解如何安裝依賴、如何創(chuàng)建連接、如何聲明表結(jié)構(gòu)與執(zhí)行查詢以及如何安全地運(yùn)行數(shù)據(jù)庫(kù)遷移含max: 1這一關(guān)鍵配置的來(lái)龍去脈。讀完本文你將能夠在一分鐘內(nèi)把一個(gè) Postgres.js 客戶端接入 Drizzle ORM并正確配置遷移環(huán)境避免常見(jiàn)的 cannot use unsafe transaction 報(bào)錯(cuò)。一、安裝依賴Postgres.js 驅(qū)動(dòng)適配層所需的依賴分為運(yùn)行時(shí)與開(kāi)發(fā)時(shí)兩部分。運(yùn)行時(shí)依賴為drizzle-orm與postgres開(kāi)發(fā)時(shí)依賴為drizzle-kit用于生成 SQL 遷移文件。倉(cāng)庫(kù) README 給出了 npm / yarn / pnpm 三種包管理器的安裝方式# npm npm i drizzle-orm postgres npm i -D drizzle-kit # yarn yarn add drizzle-orm postgres yarn add -D drizzle-kit # pnpm pnpm add drizzle-orm postgres pnpm add -D drizzle-kit在倉(cāng)庫(kù)中postgres是適配層的唯一驅(qū)動(dòng)依賴——從 driver.ts 的導(dǎo)入語(yǔ)句import pgClient, { type Options, type PostgresType, type Sql } from postgres可以看出整個(gè)適配層直接圍繞 postgres.js 的Sql客戶端實(shí)例構(gòu)建沒(méi)有其他數(shù)據(jù)庫(kù)驅(qū)動(dòng)耦合。drizzle-kit只用于開(kāi)發(fā)期生成遷移文件不需要打進(jìn)生產(chǎn)依賴。提示drizzle-orm/postgres-js子路徑導(dǎo)出的源碼位于 index.ts它對(duì)外重新導(dǎo)出driver.ts與session.ts的全部?jī)?nèi)容遷移器則單獨(dú)掛在drizzle-orm/postgres-js/migrator子路徑下對(duì)應(yīng) migrator.ts。二、建立連接兩種等效的drizzle()調(diào)用方式README 給出的最基礎(chǔ)連接方式如下import { drizzle } from drizzle-orm/postgres-js; import postgres from postgres; const client postgres(connectionString); const db drizzle(client);這段代碼做了兩件事先用 postgres.js 的postgres(connectionString)創(chuàng)建一個(gè)底層客戶端再把它交給drizzle()包裝成 Drizzle 的數(shù)據(jù)庫(kù)實(shí)例。此后db上的.select()、.insert()、.update()、.delete()等查詢構(gòu)建器都會(huì)經(jīng)由該客戶端發(fā)送到 PostgreSQL。結(jié)合 driver.ts 的源碼可以看到drizzle()實(shí)際上還支持另外幾種寫(xiě)法它們完全等價(jià)寫(xiě)法一直接傳連接字符串不顯式創(chuàng)建客戶端import { drizzle } from drizzle-orm/postgres-js; const db drizzle(connectionString); // 內(nèi)部自動(dòng)調(diào)用 postgres(connectionString)寫(xiě)法二配置對(duì)象形式可同時(shí)傳連接參數(shù)與 Drizzle 配置const db drizzle({ connection: { url: connectionString, // 這里可以放任意 postgres.js Options例如 max、ssl 等 }, // Drizzle 側(cè)配置logger / schema / casing / cache logger: true, }); // 也支持顯式傳入已創(chuàng)建的客戶端 const client postgres(connectionString, { max: 1 }); const db drizzle({ client, logger: true, });從源碼看drizzle()的參數(shù)簽名支持三類組合(client | string)、(client | string, DrizzleConfig)、以及(DrizzleConfig { connection | client })。當(dāng)傳入配置對(duì)象時(shí)源碼會(huì)解構(gòu)出connection/client并從中提取url與其余 postgres.jsOptions再調(diào)用pgClient(url, config)創(chuàng)建實(shí)例若只傳連接字符串則等價(jià)于pgClient(connectionString)。也就是說(shuō)README 中的簡(jiǎn)單寫(xiě)法只是這些重載中最直觀的一種。此外drizzle()還提供一個(gè)命名空間方法drizzle.mock(config?)用于在無(wú)真實(shí)數(shù)據(jù)庫(kù)時(shí)構(gòu)造一個(gè) mock 數(shù)據(jù)庫(kù)實(shí)例見(jiàn) driver.ts其返回值的$client類型被標(biāo)記為$client is not available on drizzle.mock()適合單元測(cè)試場(chǎng)景。關(guān)于日志、Schema 與日期解析的底層行為值得留意的是 driver.ts 中construct函數(shù)做了一些對(duì)使用者透明的處理日期解析器覆蓋postgres.js 默認(rèn)會(huì)把date/timestamp等類型解析為 JavaScriptDate對(duì)象Drizzle 為了保持對(duì)日期字符串的完整控制將類型 OID1184timestamptz、1082date、1083time、1114timestamp、1182date、1185timestamptz 數(shù)組、1115timestamp 數(shù)組、1231numeric 數(shù)組以及序列化器114json與3802jsonb統(tǒng)一替換為透?jìng)鹘馕銎鱰ransparentParser即(val) val。這意味著 Drizzle 模式下日期字段返回的是原始字符串/值而非Date對(duì)象如需Date應(yīng)在應(yīng)用層自行轉(zhuǎn)換。Logger 處理config.logger true時(shí)使用內(nèi)置DefaultLogger傳入自定義 Logger 實(shí)例時(shí)按自定義邏輯執(zhí)行。Schema關(guān)系配置若在DrizzleConfig中傳入schema表定義對(duì)象源碼會(huì)通過(guò)extractTablesRelationalConfig提取關(guān)系元數(shù)據(jù)從而支持.queryAPI 與關(guān)系查詢。緩存支持config.cache會(huì)掛載到 db 的$cache上并且當(dāng)緩存提供onMutate回調(diào)時(shí)會(huì)被注冊(cè)為緩存失效鉤子見(jiàn) driver.ts。construct最終返回的PostgresJsDatabase同時(shí)攜帶$client屬性便于需要時(shí)直接訪問(wèn)底層 postgres.js 客戶端。三、聲明表結(jié)構(gòu)并執(zhí)行查詢主文檔延伸README 將表結(jié)構(gòu)聲明指引到了主文檔本倉(cāng)庫(kù)中對(duì)應(yīng)的核心模塊位于 drizzle-orm/src/pg-core/PostgreSQL 方言核心包含table.ts、schema.ts、columns/、query-builders/等。以下是一個(gè)可直接運(yùn)行的完整示例將連接、建表、插入與查詢串起來(lái)import { drizzle } from drizzle-orm/postgres-js; import { pgTable, serial, text, timestamp } from drizzle-orm/pg-core; import postgres from postgres; const connectionString postgres://user:passwordlocalhost:5432/dbname; const client postgres(connectionString); const db drizzle(client, { logger: true }); // 1. 聲明 users 表 export const users pgTable(users, { id: serial(id).primaryKey(), name: text(name).notNull(), email: text(email).notNull().unique(), createdAt: timestamp(created_at).defaultNow(), }); // 2. 插入 await db.insert(users).values({ name: John, email: johnexample.com }); // 3. 查詢 const rows await db.select().from(users); console.log(rows);postgres.js 客戶端天然支持.unsafe()動(dòng)態(tài) SQL 與 prepared statementsDrizzle 在 session.ts 中通過(guò)PostgresJsPreparedQuery.execute()將所有構(gòu)建好的查詢語(yǔ)句與參數(shù)以client.unsafe(query, params)發(fā)送并在有字段映射需求select 或帶結(jié)果映射的更新/刪除時(shí)調(diào)用.values()獲取行數(shù)組后用mapResultRow還原為對(duì)象結(jié)果。事務(wù)則基于 postgres.js 的client.begin()實(shí)現(xiàn)見(jiàn) session.ts嵌套事務(wù)通過(guò) savepoint 支持見(jiàn) session.ts。這意味著你在db.transaction(async (tx) { ... })中寫(xiě)的代碼會(huì)獲得真正的 SQL 事務(wù)語(yǔ)義。四、運(yùn)行遷移為什么必須設(shè)置max: 1README 特別強(qiáng)調(diào)要使用 Drizzle 的migrate()運(yùn)行遷移必須在 postgres.js 的連接選項(xiàng)中設(shè)置max: 1并建議為遷移單獨(dú)創(chuàng)建一個(gè)連接實(shí)例import postgres from postgres; import { migrate } from drizzle-orm/postgres-js/migrator; const migrationsClient postgres(connectionString, { max: 1, // 關(guān)鍵遷移需要單連接避免 cannot use unsafe transaction 錯(cuò)誤 }); const db drizzle(migrationsClient); await migrate(db, { migrationsFolder: ... });max: 1的含義是讓 postgres.js 只維護(hù)一條數(shù)據(jù)庫(kù)連接。原因是 Drizzle 的遷移流程在內(nèi)部會(huì)開(kāi)啟顯式事務(wù)BEGIN/COMMIT來(lái)逐個(gè)執(zhí)行遷移 SQL而 postgres.js 的unsafe查詢?cè)谶B接池默認(rèn)max: 10模式下無(wú)法保證 same query on the same connection 的語(yǔ)義——即連接池中的不同連接無(wú)法共享同一個(gè)已開(kāi)啟的事務(wù)postgres.js 會(huì)因此拋出Cannot use unsafe transaction之類的錯(cuò)誤。將max固定為 1 即可保證遷移語(yǔ)句與事務(wù)一定落在同一條連接上。從源碼看遷移入口 migrator.ts 非常薄migrate()先調(diào)用readMigrationFiles(config)來(lái)自 migrator.ts 公共遷移基礎(chǔ)設(shè)施讀取migrationsFolder下的遷移文件然后委托給db.dialect.migrate(...)。而 PostgreSQL 方言層的實(shí)現(xiàn)位于 dialect.ts它會(huì)在目標(biāo)庫(kù)創(chuàng)建drizzleschema 與__drizzle_migrations遷移記錄表結(jié)構(gòu)為id SERIAL PRIMARY KEY, hash text NOT NULL, created_at bigint讀取已應(yīng)用的最后一條遷移記錄然后在一個(gè)事務(wù)內(nèi)按時(shí)間戳folderMillis依次執(zhí)行比已應(yīng)用記錄更新的遷移語(yǔ)句并將每條遷移的 hash 與時(shí)間寫(xiě)入記錄表。因此遷移期間的事務(wù)行為對(duì)連接數(shù)有硬性要求這就是max: 1不可省略的根本原因。另外注意README 建議為遷移單獨(dú)創(chuàng)建一個(gè)連接實(shí)例而不是復(fù)用業(yè)務(wù)查詢的客戶端——業(yè)務(wù)連接通常需要連接池保持默認(rèn)max: 10以支撐并發(fā)而遷移客戶端固定單連接即可兩者職責(zé)分離更安全。五、遷移配置項(xiàng)補(bǔ)充migrate()的第二個(gè)參數(shù)config是MigrationConfig類型除了必填的migrationsFolder遷移文件夾路徑指向drizzle-kit generate的輸出目錄還可以按需配置migrationsTable?: string—— 遷移記錄表名默認(rèn)__drizzle_migrationsmigrationsSchema?: string—— 遷移記錄表所在 schema默認(rèn)drizzle會(huì)先執(zhí)行CREATE SCHEMA IF NOT EXISTS。這兩個(gè)默認(rèn)值都可以在 dialect.ts 中確認(rèn)。示例await migrate(db, { migrationsFolder: ./drizzle, migrationsTable: migrations_log, migrationsSchema: app_migrations, });結(jié)合 integration-tests/tests/pg/postgres-js.test.ts 中的集成測(cè)試可以看到標(biāo)準(zhǔn)的遷移用法測(cè)試先用max: 1創(chuàng)建 postgres.js 客戶端再以migrate(db, { migrationsFolder: ./drizzle2/pg })執(zhí)行遷移隨后立刻能向遷移創(chuàng)建的表插入數(shù)據(jù)并查詢回讀。這驗(yàn)證了上述配置流程的真實(shí)可用性。六、遷移文件從哪來(lái)配合 drizzle-kit 的完整工作流要產(chǎn)生migrationsFolder指向的遷移文件需要借助開(kāi)發(fā)依賴drizzle-kit安裝方式見(jiàn)第一節(jié)。常規(guī)工作流為# 1. 定義 schema如第三節(jié)的 pgTable 聲明 # 2. 在 drizzle.config.ts 中配置 schema 路徑與數(shù)據(jù)庫(kù)連接 # 3. 生成遷移 SQL 文件 npx drizzle-kit generate # 4. 在應(yīng)用啟動(dòng)階段或 CI 流程執(zhí)行遷移 # await migrate(db, { migrationsFolder: ./drizzle });倉(cāng)庫(kù)中 integration-tests/drizzle2/pg/ 目錄下存放著遷移測(cè)試用的 SQL 與 JSON 文件可作為遷移產(chǎn)物形態(tài)的參考。生產(chǎn)實(shí)踐中遷移通常在服務(wù)啟動(dòng)時(shí)或部署流水線中執(zhí)行一次業(yè)務(wù)代碼則使用另一套帶連接池的客戶端實(shí)例。七、小結(jié)與最佳實(shí)踐要點(diǎn)建議依賴運(yùn)行時(shí)drizzle-ormpostgres開(kāi)發(fā)時(shí)drizzle-kit連接drizzle(client)或drizzle(connectionString)均可配置對(duì)象形式可同時(shí)傳 postgres.js Options 與 Drizzle 配置查詢?cè)赿b上使用.select/.insert/.update/.delete等構(gòu)建器事務(wù)用db.transaction()遷移必須用max: 1的獨(dú)立客戶端調(diào)用migrate()否則連接池會(huì)導(dǎo)致事務(wù)錯(cuò)誤遷移配置migrationsFolder必填migrationsTable/migrationsSchema可自定義默認(rèn)__drizzle_migrations/drizzle日期行為Drizzle 會(huì)覆蓋 postgres.js 默認(rèn)日期解析返回原始字符串/值需要Date請(qǐng)?jiān)趹?yīng)用層轉(zhuǎn)換最后回顧 README 的核心指引Postgres.js 驅(qū)動(dòng)適配的進(jìn)一步用法SQL schema 聲明、關(guān)系查詢、完整遷移文檔可繼續(xù)閱讀本倉(cāng)庫(kù) drizzle-orm/src/pg-core/ 下的源碼與類型定義以及 drizzle-orm/src/migrator.ts 的公共遷移實(shí)現(xiàn)。按照本文的配置清單操作你就能獲得一條穩(wěn)定、可生產(chǎn)使用的 Drizzle ORM × Postgres.js 鏈路。【免費(fèi)下載鏈接】drizzle-ormORM項(xiàng)目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考