Node.js使用NPM的mysql库结合Promise执行查询的问题
我在Node.js中使用NPM的mysql库,想把数据库查询改成返回Promise而非使用回调,自己写了封装代码,但遇到了问题:
我的代码实现
index.js
var result = await asyncQuery.main(connection, "SELECT * FROM tbl WHERE id = ?", "1234567890")
asyncQuery.js
var colors = require('colors') module.exports = { main: function(con, q, vars) { return new Promise((resolve, reject) => { if (!vars) { con.query(q, function (err, result) { if (err) { console.log(colors.red(err.stack)) return reject(err); } resolve(result) }) } else { con.query(q, vars, function (err, result) { if (err) { console.log(colors.red(err.stack)) return reject(err); } resolve(result) }) } }) } }
报错情况
当SQL语句没有?占位符、不传入参数时代码正常运行,但使用?传参后出现语法错误:
Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '?' at line 1
用常规回调查询完全没问题,推测是asyncQuery.js的封装有问题但找不到问题点。想请教修复方案,或者更简便的实现方式?我了解到有个Promise MySQL库,不确定它是否同时支持Promise和回调查询,如果支持的话也想尝试使用。
版本信息
- NodeJS:9.1.0
- NPM:5.5.1
- mysql库:2.15.0
一、修复现有封装的问题
你踩的这个坑其实很常见:mysql库的query方法在接收参数时,哪怕只有单个参数,也需要放在数组里传递,而你直接传了字符串"1234567890",导致库没有正确替换占位符,直接把?当成了SQL的一部分,所以触发了语法错误。
修改asyncQuery.js的逻辑,把单个参数包装成数组,同时简化分支判断:
var colors = require('colors') module.exports = { main: function(con, q, vars) { return new Promise((resolve, reject) => { // 统一处理参数:如果是单个值就包装成数组,没有参数则传undefined const queryParams = Array.isArray(vars) ? vars : (vars ? [vars] : undefined); con.query(q, queryParams, function (err, result) { if (err) { console.log(colors.red(err.stack)) return reject(err); } resolve(result) }) }) } }
这样不管你传单个值还是多参数数组,都能正确绑定到SQL的占位符上,代码也比之前简洁很多。
二、更省心的实现:用Node.js内置的util.promisify
其实不用自己手动封装Promise,Node.js内置的util模块提供了promisify方法,可以直接把回调风格的函数转换成Promise风格:
const util = require('util'); const colors = require('colors'); // 把connection.query转换成Promise版本,注意要bind绑定connection上下文 const queryAsync = util.promisify(connection.query).bind(connection); // 使用的时候直接await即可 try { // 推荐统一用数组传递参数,避免单个值的兼容问题 const result = await queryAsync("SELECT * FROM tbl WHERE id = ?", ["1234567890"]); // 有些版本也支持直接传单个值:await queryAsync("SELECT * FROM tbl WHERE id = ?", "1234567890"); console.log(result); } catch (err) { console.log(colors.red(err.stack)); }
这种方式不用自己写Promise的resolve/reject逻辑,不容易出错,代码也更简洁。
三、关于Promise MySQL库的说明
你提到的那个库确实支持同时使用Promise和回调两种风格,它是对原生mysql库的Promise化封装,同时保留了原有的回调接口:
1. Promise风格使用示例
const mysql = require('promise-mysql'); async function fetchData() { // 创建连接 const connection = await mysql.createConnection({ host: 'localhost', user: '你的用户名', password: '你的密码', database: '你的数据库' }); // 执行查询 const result = await connection.query("SELECT * FROM tbl WHERE id = ?", ["1234567890"]); // 关闭连接 await connection.end(); return result; }
2. 回调风格使用示例
const mysql = require('promise-mysql'); mysql.createConnection({/* 连接配置 */}).then(connection => { // 依然可以用回调方式调用query connection.query("SELECT * FROM tbl WHERE id = ?", ["1234567890"], (err, result) => { if (err) { console.log(colors.red(err.stack)); } else { console.log(result); } connection.end(); }); });
所以如果不想自己折腾封装,直接用这个库是非常方便的选择。
内容的提问来源于stack exchange,提问作者APixel Visuals

