一种优雅的用于Node.js的MySQL包的异步事务函数封装方式

引言

众所周知,Node.js中的 mysql 包提供的接口均为异步接口,这样虽然有效提升了性能,但在大多数业务场景中,我们需要将其转为同步方式使用。
对于常规的单次查询,可以使用一个简单的Promise完成如下封装:

const query = async function (sql, values) {return new Promise((resolve, reject) => {pool.getConnection(function (err, connection) {if (err) {reject(err);} else {connection.query(sql, values, (err, rows) => {if (err) {reject(err);} else {resolve(rows);}connection.release();});}});});
};

在上述代码中,我们将 getConnection 方法和 connection.query 方法封装为一个异步的查询方法 query,调用该函数,并传入SQL语句模板和值即可完成查询,并获得查询结果。
但是在复杂的多次事务查询,我们可能需要连续执行多条SQL语句,在执行SQL语句的间隙,可能还需要进行一系列的逻辑判断,或者执行其他可能抛出异常的操作。
因此,事务查询相比较于单次查询,封装起来就不太容易。如果不进行封装,在业务代码中就会出现大量的回调,降低代码的可读性和稳定性。
本文拟提出一种新的封装方式,将多次事务查询功能封装为一个简单的异步方法。通过传入一个简单的异步函数来完成上述功能,该函数使用起来是这样的:

await execTransaction(async (query, commit, rollback)=>{try{await query(sql1, values1)doSomethingElse();let something = await query(sql2, values2)await doSomethingAsyncElse();await query(sql3, values3)if(condition) {commit();} else {reject();}} catch(e){console.error(e);rollback();throw e;}})

可以看见,在上述代码中,在我们调用execTransaction方法的过程中,整个写法非常符合直觉,回调函数中提供了querycommitrollback三个方法,分别提供执行单次查询、提交事务和回滚事务三种操作。可以灵活随意地使用。
接下来,笔者将带你一起完成这个封装方法。

先上代码

废话不多说,首先先上代码,如果你只是想直接使用,而不想研究原理,可以直接把代码拿走

const execTransaction = (callback) => {return new Promise((resolve, reject) => {pool.getConnection(function (err, connection) {if (err) {return reject(err)}connection.beginTransaction(err => {if (err) {return reject('开启事务失败')}let _resolve = resolve;let _reject = reject;let lastResult = undefined;let query = (sql, values) => {return new Promise((resolve, reject) => {connection.query(sql, values, (e, rows, fields) => {if(e) {setTimeout(()=>{reject(e);})_reject(e);} else {lastResult = rows;resolve(rows);}})})}let rollback = () => {connection.rollback(() => {console.log('数据操作回滚')connection.release()reject(err);})}let commit = () => {connection.commit((error) => {if (error) {console.log('事务提交失败')connection.release()reject(error)} else {connection.release()  // 释放链接resolve(lastResult)}})}callback(query, commit, rollback);})});})
}

接下来,笔者将逐一分析上述代码的实现逻辑。

返回一个Promise

由于我们要实现的 execTransaction 方法是异步的,因此必然要返回一个Promise来实现异步。该Promise在resolve时表示事务执行成功,在reject时则表示事务执行失败。
在Promise中,我们首先创建一个connection,然后再调用connection.beginTransaction方法,这些方法都是mysql库提供给我们的,它们都采用了回调函数的写法。
接下来,在该方法中做文章。

Callback函数

整段代码的核心实质是一个callback函数的调用。
在调用该方法时,用户提交了一个回调函数,因此在这个函数中,回调函数一定会被调用,并传入三个方法querycommitrollback,接下来的工作就是实现这三个方法。

实现三个方法

query方法

此函数内部创建并返回一个新的Promise对象,用于执行 SQL 语句。
使用 connection.query 方法执行 SQL 查询。查询结果或错误通过回调函数返回。
如果查询出错,首先通过 setTimeout 延迟执行 reject 函数,然后通过 _reject 直接拒绝外层的 Promise
如果查询成功,将结果存储在 lastResult 中,并通过 resolve 解决内部 Promise

rollback方法

当需要回滚事务时调用,例如在查询过程中出错。
调用 connection.rollback 方法回滚所有更改,并释放数据库连接。
通过外部的 reject 函数拒绝外层的 Promise

commit 方法

用于在所有操作成功完成后提交事务。
调用 connection.commit 提交事务。如果提交时出错,打印错误消息,并通过 reject 函数拒绝外层 Promise
如果提交成功,释放连接并通过 resolve 解决外层 Promise,返回 lastResult 作为操作结果。

一个使用该方法的实际例子(由ChatGPT提出)

这是一个使用 execTransaction 方法的示例,假设我们需要在一个电商系统中添加订单数据。这个例子包括两个步骤:首先插入订单主信息,然后插入订单的详细商品信息。如果其中任何一个步骤失败,我们需要回滚整个事务以保持数据的一致性。

// 引入已定义的 execTransaction 方法
const { execTransaction } = require('./path_to_your_transaction_file');// 使用 execTransaction 方法执行事务
execTransaction(async (query, commit, rollback) => {try {// 第一步:插入订单主信息const orderSql = 'INSERT INTO orders (customer_id, order_date, total) VALUES (?, ?, ?)';const orderValues = [123, '2023-07-19', 299.99];const orderResult = await query(orderSql, orderValues);console.log('订单主信息已添加', orderResult);// 第二步:插入订单详情const detailSql = 'INSERT INTO order_details (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)';const orderDetails = [[orderResult.insertId, 1, 2, 59.99],  // 商品1[orderResult.insertId, 2, 1, 180.00]  // 商品2];for (let detail of orderDetails) {const detailResult = await query(detailSql, detail);console.log('订单详细信息已添加', detailResult);}// 如果所有操作成功,提交事务await commit();console.log('事务已成功提交');} catch (error) {console.log('事务处理中出错,进行回滚', error);// 如果出现错误,回滚事务await rollback();}
}).then(() => {console.log('所有操作完成');
}).catch((error) => {console.log('事务处理失败', error);
});

在这个例子中:

  1. 我们首先尝试插入订单的主信息。 如果主信息插入成功,我们接着插入订单详细的商品信息。 对于每一步操作,我们都使用 query 函数进行数据库查询,它会在成功时返回结果,在失败时抛出异常。 如果所有数据库操作都成功,我们调用 commit 函数提交事务。
  2. 如果任何一个数据库操作失败,我们捕获异常,并调用 rollback 函数回滚整个事务,以确保数据库状态的一致性和准确性。
  3. execTransaction本身返回的是一个 Promise,我们可以用 .then.catch来处理正常完成或发生错误的情况。

这样,整个事务操作的逻辑就非常清晰,并且能够有效地处理可能出现的错误。

总结

本文提出了一种优雅的基于Node.js的MySQL包的异步事务函数封装方式,该函数接收一个回调函数 callback 作为参数,并返回一个 Promise 对象,以支持异步操作和错误处理。
使用该函数可以使得进行复杂的数据库操作时,在一个封闭的事务中顺序执行多个查询,并且只有在所有操作成功完成后才提交事务,否则回滚所有更改。这有助于保证数据的一致性和完整性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/48083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云盾占用资源的问题AliYunDun,AliYunDunUpdate

目录 1.关闭AliYunDunUpdate,AliYunDun,AliYunDunMonitor。 2.发现报错如下 3.打开阿里云安全中心控制台 4.成功解决 2.开启云盾命令 “如果您在解决类似问题时也遇到了困难,希望我的经验分享对您有所帮助。如果您有任何疑问或者想分享您…

哪种SSL证书可以快速签发保护http安全访问?

用户访问网站,经常会遇到访问http网页时,提示网站不安全或者不是私密连接的提示,因为http是使用明文传输,数据传输中可能被篡改,数据不被保护,通常需要SSL证书来给数据加密。 SSL证书的签发速度&#xff0…

使用JWT双令牌机制进行接口请求鉴权

在前后端分离的开发过程中,前端发起请求,调用后端接口,后端在接收请求时,首先需要对收到的请求鉴权,在这种情况先我们可以采用JWT机制来鉴权。 JWT有两种机制,单令牌机制和双令牌机制。 单令牌机制服务端…

工作记录7.19

xss应当用filter进行过滤, null值校验 版本升级的时候,往往会发生代码冲突,这时候找到错误点,注释掉再看。 springboot2.7.15与springfox的Swagger3.0产生路径冲突 Swagger3.0 与spring boot2.7x 整合避免swagger2.0与boot2.7冲突…

环信IM x 亚马逊云科技,助力出海企业实现可靠通讯服务

随着全球化进程的加速,越来越多的企业选择出海,拓展国际市场。然而,面对不同国家和地区的用户,企业在即时通讯方面遇到了诸多挑战。为了帮助企业克服这些困难,环信IM与亚马逊云科技强强联手,共同推出了一套…

LDR6020:重塑iPad一体式有线键盘体验的创新力量

在移动办公与娱乐日益融合的时代,iPad凭借其强大的性能和便携性,成为了众多用户不可或缺的生产力工具。然而,为了进一步提升iPad的使用体验,一款高效、便捷的键盘成为了不可或缺的配件。今天,我们要介绍的,…

关于黑马商城微服务拆分

1.拆分流程 大差不差分为 创建module-依赖-启动类-配置yml文件-抽取代码-数据库-配置启动项-测试 2.微服务的好处 在测试的时候明显感觉到微服务的好处 不用启动所有的项目 只是单纯一个模块比如支付就可以自己调试 非常便捷而且易开发 抽取的公共模块api也不用启动就能测试 …

机器学习-20-基于交互式web应用框架streamlit的基础使用教程

参考简洁而优雅地展示你的算法和数据——streamlit教程(一) 原理介绍与布局控制 参考Streamlit 讲解专栏(二):搭建第一个应用 Streamlit 讲解专栏(三):两种方案构建多页面 Streamlit 讲解专栏(五):探索强大而灵活的 st.write() 函数 1 streamlit 1.1 运行原理 im…

免费缺陷管理工具深度评测与使用心得

国内外主流的10款缺陷跟踪工具对比:PingCode、Worktile、滴答清单、CalendarTask、专注清单、Todo清单、Jira、Bugzilla、MantisBT、Redmine。 在寻找合适的缺陷管理工具时,很多团队面临一个共同的挑战:如何在有限的预算内找到既高效又易于使…

探索光影魔法:WebKit中的CSS文本阴影效果

探索光影魔法:WebKit中的CSS文本阴影效果 CSS文本阴影(Text Shadow)是Web设计中一种创造视觉深度和强调文本的流行技术。它通过在文本下方添加模糊的阴影,为网页元素增添立体感。WebKit作为许多现代浏览器的渲染引擎,…

深入浅出WebRTC—ALR

ALR(Application Limited Region)指的是网络传输过程中,由于应用层的限制(而非网络拥塞)导致带宽未被充分利用的情况。在这种情况下,应用层可能因为处理能力、手动配置或其他因素无法充分利用可用带宽&…

Spring Authorization Server 自定义 OAuth2 密码模式返回数据结构优化

前言 对接了自定义密码模式,但是返回的数据结构不符合要求 我们需要改成下面格式 开始 我假设你已经对接好了自定义密码功能,不会的话看下面文章 Spring Authorization Server 1.1 扩展实现 OAuth2 密码模式与 Spring Cloud 的整合实战(上…

ChatGPT对话:Windows如何将Python训练模型转换为TensorFlow.js格式

【编者按】编者目前正在做手机上的人工智能软件,第一次做这种工作,从一些基本工作开始与ChatGPT交流。对初学者应该有帮助。 一天后修改文章补充内容: 解决TensorFlow 2.X与TensorFlow Decision Forests版本冲突问题: 在使用tens…

像 MvvmLight 一样使用 CommunityToolkit.Mvvm 工具包

文章目录 简介一、安装工具包二、实现步骤1.按照MvvmLight 的结构创建对应文件夹和文件2.编辑 ViewModelLocator3.引用全局资源二、使用详情1.属性2.命令3. 消息通知4. 完整程序代码展示运行结果简介 CommunityToolkit.Mvvm 包(又名 MVVM 工具包,以前称为 Microsoft.Toolkit…

学习计算机

不要只盯着计算机语言学习,你现在已经学习了C语言和Java,暑假又规划学习Python,最后你掌握的就是计算机语言包而已。 2. 建议你找一门想要深挖的语言,沿着这个方向继续往后学习知识就行。计算机语言是学不完的,而未来就…

Programming Studio COSC2803

You must divide the implementation so that each member of the group contributes to every level. iuww520iuww520iuww520iuww520iuww520iuww520iuww520iuww520 Specifically:  Each person must implement one LEVEL 1 (GREEN) sub-task.  Each person must …

[数据集][目标检测]拐杖检测数据集VOC+YOLO格式2778张1类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2778 标注数量(xml文件个数):2778 标注数量(txt文件个数):2778 标注…

【iOS】—— isMemberOfClass isKindOfClass以及源码

【iOS】—— isMemberOfClass & isKindOfClass以及源码 isa指针示例源码解析:isKindOfClass:源码解析(实例方法和类方法)isMemberOfClass:源码解析(实例方法和类方法)源码分析总结&#xff…

【中项】系统集成项目管理工程师-第2章 信息技术发展-2.2新一代信息技术及应用-2.2.3大数据与2.2.4区块链

前言:系统集成项目管理工程师专业,现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试,全称为“全国计算机与软件专业技术资格(水平)考试”&…

@Configuration的proxyBeanMethods

作用:用于控制配置类的代理行为,确定配置类中的 Bean 方法是否被代理,从而影响 Bean 的创建和管理方式 比较 proxyBeanMethods true 默认行为: 当 proxyBeanMethods 设置为 true(默认值)时,Spring 会为配置类创建一…