項目地址: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
問題解決,撒花!(寫字太累了草草收尾就這樣吧)