告别回调地狱,在Node里优雅的访问MySQL(二)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: Node.js 环境里面访问 MySQL 的默认方式,采用了古老的回调方式,这样很容易产生回调地狱。那么如何避免呢?这里介绍一种基于 Promise 的封装方式,可以避免回调地狱,并且支持事务访问。

await 方式

我们是否可以用 await 的方式实现一下开启事务的代码呢?尝试了一下,也是可以的,虽然这么做没有什么实际意义。


/**
* 开启事务,await 的方式
*/
async beginTransaction() {
console.log('★ 开启事务,await 模式')
let _cn = null
try {
_cn = awaitthis._poolCreateConnection()
awaitthis._beginStran(_cn)
} catch(e) {
console.log('async 开启事务出错:', e)
}
return _cn
}


没有了回调方式,看起来是不是舒服多了?为啥说没啥实际意义呢?因为这种方式,要求调用者也必须使用 await 的方式,有点强制性。而上面那个函数(begin)既可以用 Promise 的方式,也可以用 await 的方式,即满足需求也比较灵活。


提交事务

开启事务,执行各种操作后,需要提交事务,那么我们再做一个提交事务的功能。


/**
* 提交一个事务
* @param { connection } cn 开启事务时创建的连接对象
*/
commit(_cn) {
const myPromise = newPromise((resolve, reject) => {
// 提交事务
_cn.commit((err) => {
if(err) {
console.log('事务提交失败', err)
reject(err)
} else {
resolve()
}
})
})
return myPromise
}


关闭连接、归还连接对象

如果没有开始事务,直接关闭连接即可,如果开启事务,需要把链接对象放回池子。二者写法有点差别。

/**
* 关闭数据库
* @param { connection } cn 开启事务时创建的连接对象
*/
close(_cn = null) {
if (_cn !== null ) {
// 归还连接对象。
_cn.release()
} else {
// 关闭连接
this.db.end((err) => {
if(err) {
console.error('关闭连接发生错误:', err)
} else {
console.log('\n[MySQL 已经关闭数据库:]\n')
}
})
}
}


关闭连接池

如果开启事务的话,还需要关闭连接池。

/**
* 关闭池子
*/
closePool() {
this.pool.end((err) => {
if(err) {
console.error('关闭连接发生错误:', err)
} else {
console.log('\n[MySQL 已经关闭连接池:]\n')
}
})
}

封装SQL语句


核心操作,上面的 help 就可以实现了,下面要封装SQL,避免手撸SQL的尴尬。思路有点像ORM,但是又不完全是ORM,采用 meta + model 的形式拼接参数化的SQL语句。

insert —— addModel

从SQL语句的角度来看,是 insert into table,从对象的角度来看,就是添加了一个model。不管怎么说,都是需要一个SQL语句才行,如果手撸的话,既麻烦又容易出错,那么怎么办呢?我们可以先设定一个 meta 进行描述,然后写个函数拼接即可。

  • meta
{
"tableName": "node_user",
"idKey": "id",
"cols": {
"name": "",
"age": ""
} 
}


由表名、主键字段名、需要的字段集合组成,这个看起来好像是一个表、字段的结构,其实并不是,区别在于字段集合的组成方式。

一般情况是基于表建立的 model,需要把表的字段都加上,不能少。

但是这里的 meta 并不要求把表里面的字段都加上,而是根据业务需求设置字段,业务需要哪些字段就放置哪些字段,不需要的可以不放。

另外一个表可以设置多种这样的 meta,完全依据业务需求来决定。

  • 实现代码

/data-add.js

/**
* 实现添加数据的功能。拼接 insert 的 SQL语句
* @param { MySQLHelp } help 访问数据库的实例
* @param { Object } meta 表、字段
* @param { Object } model 数据
* @param { connection } cn 如果使用事务的话,需要传递开启事务时创建的连接对象
* @returns 添加记录的ID
* * meta 结构:
* * * tableName:'', 表名
* * * cols:{colName: '类型'}, josn 字段需要标记 
* * model 结构:
* * * colName: value 
*/
function addData(help, meta, model, cn = null) {
// 拼接添加用的SQL语句,
// 提交SQL语句
const myPromise = newPromise((resolve, reject) => {
// sql = 'INSERT INTO aaa set aaacol = ? '
// 获取字段名称和值的数组
const { colNames, params } = help.createTableCols(meta, model)
const sql = `INSERT INTO ${meta.tableName} SET ${colNames.join(',')} `
console.log('addData --- sql:', sql, params)
const _cn = cn === null ? help.db : cn
help.query(sql, params, _cn)
.then((res) => {
// console.log('添加数据成功:', res.insertId)
resolve(res.insertId)
})
.catch((res) => {
// 出错了
reject(res)
})
})
return myPromise
}
module.exports = addData

如果使用事务的话,需要传递一个链接对象进来,否则使用内部默认的连接对象。

  • help  实现基础功能的实例。
  • meta  上面说到的表、字段的描述
  • model  要添加的数据


update

同上(代码雷同就不贴了),拼接 update 的SQL语句,加上where即可。

每一个小功能都做成了独立的 js 文件。其实一开始想做一个大的class,后来觉得代码太长不好维护,于是想做成子类的形式,但是想想似乎没啥必要,除了少传递一个help参数之外好像没啥区别。所以最后决定采用这种方式,


delete

同上,只需要表名和主键字段即可。


/**
* 实现删除数据的功能。拼接 DELETE FROM 的 SQL语句
* @param { MySQLHelp } help 访问数据库的实例
* @param { Object } info 表、字段
* @param { number|string } id 数据
* @returns 影响的记录数
* * info 结构:
* * * tableName:'', 表名
* * * idKey '', 主键名称
* * * cols:{colName: '类型'}, josn 字段需要标记 
* * id :number | string
*/
function deleteData(help, info, id, cn = null) {
// 拼接 修改 用的SQL语句,
const myPromise = newPromise((resolve, reject) => {
const sql = `DELETE FROM ${info.tableName} WHERE ${info.idKey} = ? `
const _cn = cn === null ? help.db : cn
help.query(sql, [id], _cn)
.then((res) => {
// 删除成功,返回影响行数
resolve(res.affectedRows)
})
.catch((res) => {
// 出错了
reject(res)
})
})
return myPromise
}
module.exports = deleteData
相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1天前
|
关系型数据库 MySQL 数据库连接
解决 mysql8.0 ERROR 1045 (28000): Access denied for user ‘ODBC‘@‘localhost‘ (using password: NO)用户访问拒绝
解决 mysql8.0 ERROR 1045 (28000): Access denied for user ‘ODBC‘@‘localhost‘ (using password: NO)用户访问拒绝
14 5
解决 mysql8.0 ERROR 1045 (28000): Access denied for user ‘ODBC‘@‘localhost‘ (using password: NO)用户访问拒绝
|
13天前
|
运维 监控 关系型数据库
Serverless 应用引擎产品使用之在阿里云函数计算(FC)中,要访问另一个账号的rds配置rds的白名单如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
30 0
|
15天前
|
JavaScript 前端开发 关系型数据库
node+vue3+mysql前后分离开发范式——实现视频文件上传并渲染
node+vue3+mysql前后分离开发范式——实现视频文件上传并渲染
25 1
|
15天前
|
JavaScript 前端开发 API
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
36 1
|
16天前
|
安全 关系型数据库 MySQL
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
22 3
|
16天前
|
安全 关系型数据库 Linux
centos7_安装mysql8(局域网访问navicat连接)
centos7_安装mysql8(局域网访问navicat连接)
23 1
|
1月前
|
SQL 关系型数据库 MySQL
mysql查询语句的访问方法const、ref、ref_or_null、range、index、all
mysql查询语句的访问方法const、ref、ref_or_null、range、index、all
|
1月前
|
关系型数据库 MySQL Docker
Docker从容器中项目如何访问到宿主机MYSQL
Docker从容器中项目如何访问到宿主机MYSQL
161 0
|
2月前
|
关系型数据库 MySQL 数据库
docker容器访问宿主机mysql数据库
docker容器访问宿主机mysql数据库
78 0
|
2月前
|
安全 关系型数据库 网络安全
rds公共网络/公网访问
RDS公网访问允许用户通过互联网连接云数据库,但默认关闭以确保安全。需手动开启并配置公网IP或域名,使用时需注意安全风险,如设置严格防火墙规则、启用SSL/TLS加密和强化身份验证。公网访问可能产生带宽、IP及附加服务费用。内网访问是更安全、经济的选择,除非特定场景(如使用Linked Server功能)需公网访问。在实施时,应权衡安全、成本和需求。
28 1