如果您編寫 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} `; } } </void></number>
服務模組
服務模組包含業務邏輯,例如取得餘額、驗證餘額以及保存更新後的餘額。
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 <p>在 bill_customer 函數中,我們先使用 get_balance 來擷取使用者的餘額。然後,我們檢查餘額是否足夠並使用 set_balance 更新它。 </p> <h2> 交易 </h2> <p>上述程式碼的問題在於,在取得和更新時間之間的餘額可能會改變。為了避免這種情況,我們需要使用<strong>交易</strong>。您可以透過兩種方式處理這個問題:</p>
- 在儲存庫模組中嵌入業務邏輯:這種方法將業務規則與資料庫操作耦合在一起,使測試變得更加困難。
- 在服務模組中使用交易:這可能會導致抽象洩漏,因為服務模組需要明確管理資料庫會話。
相反,我建議採用更乾淨的方法。
交易代碼
處理事務的一個好方法是建立一個在事務中包裝回調的函數。此函數提供了一個會話對象,該對像不會暴露不必要的內部細節,從而防止洩漏抽象。會話物件傳遞給事務中所有與資料庫相關的函數。
實作方法如下:
import { Injectable } from '@nestjs/common'; import postgres, { TransactionSql } from 'postgres'; export type SessionObject = TransactionSql<record 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} `; } } </void></number></t></t></record>
在此範例中,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} `; } } </void></number>
在 bill_customer_transactional 函數中,我們呼叫 run_in_session 並以會話物件作為參數傳遞回調,然後將此參數傳遞給我們呼叫的儲存庫的每個函數。這可確保 get_balance 和 set_balance 在同一事務中運作。如果兩次呼叫之間的餘額發生變化,交易將失敗,從而保持資料完整性。
結論
使用事務有效地確保您的資料庫操作保持一致,尤其是在涉及多個步驟時。我概述的方法可以幫助您在不洩漏抽象的情況下管理事務,從而使您的程式碼更易於維護。嘗試在您的下一個專案中實現此模式,以保持邏輯清晰和資料安全!
感謝您的閱讀!
?喜歡這篇文章別忘了按讚嗎?
聯絡方式
如果您喜歡這篇文章,請隨時在 LinkedIn 上聯繫並在 Twitter 上關注我。
訂閱我的郵件清單:https://sergedevs.com
一定要喜歡並關注嗎?
以上是如何在 TypeScript 中編寫事務資料庫調用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

簡單JavaScript函數用於檢查日期是否有效。 function isValidDate(s) { var bits = s.split('/'); var d = new Date(bits[2] '/' bits[1] '/' bits[0]); return !!(d && (d.getMonth() 1) == bits[1] && d.getDate() == Number(bits[0])); } //測試 var

本文探討如何使用 jQuery 獲取和設置 DOM 元素的內邊距和外邊距值,特別是元素外邊距和內邊距的具體位置。雖然可以使用 CSS 設置元素的內邊距和外邊距,但獲取準確的值可能會比較棘手。 // 設定 $("div.header").css("margin","10px"); $("div.header").css("padding","10px"); 你可能會認為這段代碼很

本文探討了十個特殊的jQuery選項卡和手風琴。 選項卡和手風琴之間的關鍵區別在於其內容面板的顯示和隱藏方式。讓我們深入研究這十個示例。 相關文章:10個jQuery選項卡插件

發現十個傑出的jQuery插件,以提升您的網站的活力和視覺吸引力!這個精選的收藏品提供了不同的功能,從圖像動畫到交互式畫廊。讓我們探索這些強大的工具:相關文章:1

HTTP-Console是一個節點模塊,可為您提供用於執行HTTP命令的命令行接口。不管您是否針對Web服務器,Web Serv

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

當div內容超出容器元素區域時,以下jQuery代碼片段可用於添加滾動條。 (無演示,請直接複製到Firebug中) //D = document //W = window //$ = jQuery var contentArea = $(this), wintop = contentArea.scrollTop(), docheight = $(D).height(), winheight = $(W).height(), divheight = $('#c


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver Mac版
視覺化網頁開發工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。