如何在 TypeScript 中編寫事務資料庫調用
如果您編寫 Web 服務,您很可能會與資料庫互動。有時,您需要進行必須以原子方式應用的更改 - 要么全部成功,要么全部失敗。這就是事務的用武之地。在本文中,我將向您展示如何在程式碼中實現事務,以避免抽象洩漏問題。
例子
一個常見的例子是處理付款:
- 您需要取得使用者的餘額並檢查是否足夠。
- 然後,您更新餘額並保存。
結構
通常,您的應用程式將有兩個模組來將業務邏輯與資料庫相關程式碼分開。
儲存庫模組
此模組處理所有與資料庫相關的操作,例如 SQL 查詢。下面,我們定義兩個函數:
- get_balance — 從資料庫中檢索使用者的餘額。
- set_balance — 更新使用者的餘額。
import { Injectable } from '@nestjs/common'; import postgres from 'postgres'; @Injectable() export class BillingRepository { constructor( private readonly db_connection: postgres.Sql, ) {} async get_balance(customer_id: string): Promise<number | null> { const rows = await this.db_connection` SELECT amount FROM balances WHERE customer_id=${customer_id} `; return (rows[0]?.amount) ?? null; } async set_balance(customer_id: string, amount: number): Promise<void> { await this.db_connection` UPDATE balances SET amount=${amount} WHERE customer_id=${customer_id} `; } }
服務模組
服務模組包含業務邏輯,例如取得餘額、驗證餘額以及保存更新後的餘額。
import { Injectable } from '@nestjs/common'; import { BillingRepository } from 'src/billing/billing.repository'; @Injectable() export class BillingService { constructor( private readonly billing_repository: BillingRepository, ) {} async bill_customer(customer_id: string, amount: number) { const balance = await this.billing_repository.get_balance(customer_id); // The balance may change between the time of this check and the update. if (balance === null || balance < amount) { return new Error('Insufficient funds'); } await this.billing_repository.set_balance(customer_id, balance - amount); } }
在 bill_customer 函數中,我們先使用 get_balance 來擷取使用者的餘額。然後,我們檢查餘額是否足夠並使用 set_balance 更新它。
交易
上述程式碼的問題在於,在取得和更新時間之間的餘額可能會改變。為了避免這種情況,我們需要使用交易。您可以透過兩種方式處理這個問題:
- 在儲存庫模組中嵌入業務邏輯:這種方法將業務規則與資料庫操作耦合在一起,使測試變得更加困難。
- 在服務模組中使用交易:這可能會導致抽象洩漏,因為服務模組需要明確管理資料庫會話。
相反,我建議採用更乾淨的方法。
交易代碼
處理事務的一個好方法是建立一個在事務中包裝回調的函數。此函數提供了一個會話對象,該對像不會暴露不必要的內部細節,從而防止洩漏抽象。會話物件傳遞給事務中所有與資料庫相關的函數。
實作方法如下:
import { Injectable } from '@nestjs/common'; import postgres, { TransactionSql } from 'postgres'; export type SessionObject = TransactionSql<Record<string, unknown>>; @Injectable() export class BillingRepository { constructor( private readonly db_connection: postgres.Sql, ) {} async run_in_session<T>(cb: (sql: SessionObject) => T | Promise<T>) { return await this.db_connection.begin((session) => cb(session)); } async get_balance( customer_id: string, session: postgres.TransactionSql | postgres.Sql = this.db_connection ): Promise<number | null> { const rows = await session` SELECT amount FROM balances WHERE customer_id=${customer_id} `; return (rows[0]?.amount) ?? null; } async set_balance( customer_id: string, amount: number, session: postgres.TransactionSql | postgres.Sql = this.db_connection ): Promise<void> { await session` UPDATE balances SET amount=${amount} WHERE customer_id=${customer_id} `; } }
在此範例中,run_in_session 函數啟動一個交易並在其中執行回呼。 SessionObject 類型抽象資料庫會話以防止洩漏內部細節。所有與資料庫相關的函數現在都接受會話對象,確保它們可以參與相同事務。
更新的服務模組
服務模組更新為槓桿交易。它看起來像這樣:
import { Injectable } from '@nestjs/common'; import postgres from 'postgres'; @Injectable() export class BillingRepository { constructor( private readonly db_connection: postgres.Sql, ) {} async get_balance(customer_id: string): Promise<number | null> { const rows = await this.db_connection` SELECT amount FROM balances WHERE customer_id=${customer_id} `; return (rows[0]?.amount) ?? null; } async set_balance(customer_id: string, amount: number): Promise<void> { await this.db_connection` UPDATE balances SET amount=${amount} WHERE customer_id=${customer_id} `; } }
在 bill_customer_transactional 函數中,我們呼叫 run_in_session 並以會話物件作為參數傳遞回調,然後將此參數傳遞給我們呼叫的儲存庫的每個函數。這可確保 get_balance 和 set_balance 在同一事務中運作。如果兩次呼叫之間的餘額發生變化,交易將失敗,從而保持資料完整性。
結論
使用事務有效地確保您的資料庫操作保持一致,尤其是在涉及多個步驟時。我概述的方法可以幫助您在不洩漏抽象的情況下管理事務,從而使您的程式碼更易於維護。嘗試在您的下一個專案中實現此模式,以保持邏輯清晰和資料安全!
感謝您的閱讀!
?喜歡這篇文章別忘了按讚嗎?
聯絡方式
如果您喜歡這篇文章,請隨時在 LinkedIn 上聯繫並在 Twitter 上關注我。
訂閱我的郵件清單:https://sergedevs.com
一定要喜歡並關注嗎?
以上是如何在 TypeScript 中編寫事務資料庫調用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

Python和JavaScript在開發環境上的選擇都很重要。 1)Python的開發環境包括PyCharm、JupyterNotebook和Anaconda,適合數據科學和快速原型開發。 2)JavaScript的開發環境包括Node.js、VSCode和Webpack,適用於前端和後端開發。根據項目需求選擇合適的工具可以提高開發效率和項目成功率。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。
