项目地址:tedious
安装
npm i --save tedious
配置 config.js
基础的配置信息包括
server -- 数据库所在服务器,
userName -- 用户名,
password -- 密码,
options { database:' '} 数据库名等配置信息,详细的配置在 官方API 可以看到
let Connection= require('tedious').Connection
let Request = require('tedious').Request
let connectionCfg = {
server: '127.0.0.1',
userName: 'yourname',
password: 'yourpassword',
options: { database: 'database' }
}
let connection = new Connection('connectionCfg')
connection.on('connect', function (err){
if (!err) {
executeStatement(querySql)
}
})
function executeStatement ( querySql) {
let request = new Request(querySql , (err, rowCount)=>{
if (err) {
console.error(err)
return; //创建 request 实例失败
}
})
var result = [];
request.on('row', function(columns,idx) {
var obj = {}
columns.forEach( function(column) {
if(column.value !== null){
var key = column.metadata.colName
var val = column.value
obj[key] = val
}
});
result.push(obj)
})
request.on('done', function ( rowCount, more, rows) {
return result
})
}
代码写到这里感觉还是一片顺利,实际上已经有一两个小坑需要提醒一下了。
-
request
的row
事件,回调函数中的参数 columns 受config.options.useColumnNames
影响,默认为false
,设置为true
或者其他任何类型转换会判断为true
的值时,返回的结果为object
类型,这里的设置决定了遍历方法。详细的config
配置说明在 这里 -
request
的done
事件,官方的说法是:创建Request
实例时使用普通的sql查询时,查询完毕会触发该事件,请求类型为存储过程的话,触发doneInProc
或doneProc
事件,实际在在使用过程中,即使将普通 sql 查询语句拼接成字符串传入Request
实例中也不会触发done
事件,建议还是使用doneProc
事件。语法如下:
request.on('doneProc', function (rowCount, more, returnStatus, rows) { });
判断callback中的more == false
时,视为请求完成。
常见问题
报错
RequestError:requests can only be made in the LoggedIn state, not the SentClientRequest state
在我将配置项用作 module 引入时,经常碰到这个问题: 进入页面->触发数据库操作->返回上一页->再返回该页时,就得到一个 Internal Server Error
的500页面。
翻 issue 时发现主要是因为一个问题 , 套用作者原话:issue地址
only one request can be performed on a connection at a time.
在同一时间同一个连接只能执行一个请求
具体到我的代码中,和提出 issue 这位用户很相似。同样是向外暴露了一个execute引用一个该用户核心代码和作者的解答
controller.execute = function(session, admin, sqlCommand) {
var MSSqlConnection = require('./MSSqlConnection');
MSSqlConnection.on('connected', function(connection){
_performQuery(connection, sqlCommand.toString());
});
...
}
作者的解释
Your
MSSqlConnection
variable is a singleton, becauserequire
caches modules. So when this code is called for the second page request, a second listener is added for theMSSqlConnection's connected
event. When the connected event is emitted fromMSSqlConnection
, both listeners are called. This results in two calls to the Tedious Connection object's execSql function. The second call occurs before the first request has completed, and the state machine in Connection detects and reports the problem.
大概的意思就是,第二次进入页面时,向 connected
事件加了一个监听器,connected
事件触发时,两个监听器都被调用,第二个请求发起时,第一个请求还没有结束(还处于 sentClientRequest
的state 中),所以就报错了。
the
Request's
callback signifies the completion of the request, and that another request may not be made until it is called.
创建 Request
实例的意味着该请求完成,在这之前,其他请求不会被调用。
你很难去确定前面的请求是否已经是完成状态,是否可以执行下一个request
,之前我不做模块化的时候,将所有请求方法和connect
全部放到一个对象中,没有出现过这个问题,代码结构类似这样
let nodeSql = {
connect: function () {
len connection = new Connection(config)
connection.on('connect' , funciton(){
nodeSql.connection = connection
....
})
connection.on('end', function(){
nodeSql.connection = null
})
connection.on('end', function(){
nodeSql.connection = null
})
},
execute: function (querySql) {
if(this.connection) {
return nodeSql.executeStatement(querySql)
}else{
return Promise.resolve(this.connect())
.then(()=>nodeSql.executeStatement(querySql))
}
}
}
这样的代码当时没遇到问题,所以我有点懵逼,只能认定是 module的问题,为了代码结构清晰又不能放弃 module。好在作者还提供了连接池的使用方式,使用也非常简单。 tedious-connection-pool
问题解决,撒花!(写字太累了草草收尾就这样吧)
网友评论