如果您编写 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中文网其他相关文章!

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

记事本++7.3.1
好用且免费的代码编辑器

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

禅工作室 13.0.1
功能强大的PHP集成开发环境